Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

flush returns 500 when SQLite write hits the busy timeout

Đang mở
#498 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức phù hợp với người mới
55/100
Loại issue
Lỗi
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python, sqlalchemy, sqlite
Lĩnh vực
backend, database

Hướng nghiên cứu

Start with grep -rn "OperationalError" src/everos/core/persistence/sqlite/ to find where lock timeouts escape, and read _apply_pragmas in core/persistence/sqlite/engine.py. The fix either retries database is locked with bounded backoff or maps it to a 503 with Retry-After next to the existing EXTERNAL_SERVICE_UNAVAILABLE handling. Done means a test that holds a write transaction past busy_timeout_ms on a second connection gets a retryable response instead of a 500. The design choice between retry and status mapping is not settled in the issue, so confirm it with maintainers first.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

What happened

During a 10-hour soak at sustained load, one POST /api/v2/memory/flush returned
500 INTERNAL_ERROR. The cause was SQLite lock contention:

2026-10-13T14:12:17Z [error] unhandled_exception
    exception_type=OperationalError path=/api/v2/memory/flush

OperationalError: (sqlite3.OperationalError) database is locked

The write waited out PRAGMA busy_timeout (default busy_timeout_ms = 5000) and
the error propagated uncaught through the middleware stack to the client.

Why this is worth fixing

Lock contention under concurrent writers is expected and retryable. The
problem is the status it is reported as: a caller receiving 500 INTERNAL_ERROR
has no way to tell "the server is broken" from "try again in a moment", so the
only safe client behaviour is to give up or to retry blindly on a code that
normally means retrying is pointless.

The other three failures in the same run were all external-provider issues and
were surfaced correctly as 503 EXTERNAL_SERVICE_UNAVAILABLE — so the contrast
is visible within one soak: a transient, retryable condition we own is the one
reported as an internal fault.

How often

Run Platform Duration Writes Occurrences
soak, real providers, 2 writers + 4 readers macOS 15.6 (arm64) 10.03 h 3003 1
soak, stub providers, high write rate Ubuntu (x86_64) 10 h 14398 0
soak, real providers, 2 writers + 4 readers Windows 11 (x86_64) 2.08 h 570 0

Roughly 0.03% of writes on the one run that hit it. Rare, but it scales with
concurrency, and the hosted/multi-writer deployments are exactly where it gets
more likely.

Configuration in effect

Defaults, nothing tuned:

Setting Value
sqlite.journal_mode WAL
sqlite.busy_timeout_ms 5000
sqlite.pool_size 5
sqlite.max_overflow 10
sqlite.pool_timeout_seconds 30.0

The PRAGMAs are applied per connection in
core/persistence/sqlite/engine.py::_apply_pragmas, and they are being applied
correctly — this is not a misconfiguration, the writer genuinely waited 5s.

Suggested direction

Either would remove the misleading status; the first also removes the failure:

  1. Retry the write inside the persistence layer on
    sqlite3.OperationalError whose message is database is locked, with a
    short bounded backoff. grep -rn "OperationalError" src/everos/core/persistence/sqlite/
    currently returns no handler, so every lock timeout becomes an unhandled
    exception.
  2. Map it to a retryable status — 503 with Retry-After, alongside the
    existing EXTERNAL_SERVICE_UNAVAILABLE mapping — so a caller can act on it.

Raising busy_timeout_ms alone would only move the threshold, not change what
the caller sees when it is crossed.

Reproducing

Not reproducible on demand — it took 10 hours of sustained concurrent load to
surface once. A targeted test is cheaper than a soak: hold a write transaction
open past busy_timeout_ms on a second connection and assert the response is
retryable rather than 500.

Evidence

  • Server log: unhandled_exception at 2026-10-13T14:12:17.283268Z
  • Soak result: totals.errors = 4, of which this is the only non-external one
  • Commit under test: c36a986
  • Note: the rendered traceback captured only the middleware frames
    (core/middleware/request_id.py:51, core/middleware/profile.py:54) above
    the SQLAlchemy/aiosqlite stack, so the exact repository call site is not in
    the log.
Ngôn ngữ chính
Python
Star
13.3k
Fork
926
Merge trung bình
17 giờ 8 phút
Pull request đã merge (30 ngày)
32

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của EverMind-AI/EverOS

Tất cả issue của EverMind-AI/EverOS

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.