Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

flush returns 500 when SQLite write hits the busy timeout

Abierto
#498 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
3/5
Tiempo estimado
1-2 días
Aptitud para principiantes
55/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
python, sqlalchemy, sqlite
Área
backend, database

Línea de trabajo

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.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.
Lenguaje dominante
Python
Estrellas
13.3k
Forks
926
Merge medio
17 h 8 min
PR fusionados (30 d)
32

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de EverMind-AI/EverOS

Todos los issues de EverMind-AI/EverOS

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.