Report #78653
[bug\_fix] database is locked \(SQLITE\_BUSY\)
Enable Write-Ahead Logging \(WAL\) mode via 'PRAGMA journal\_mode=WAL' to allow concurrent readers and a single writer, and implement a busy timeout handler \(PRAGMA busy\_timeout=5000\) to allow waiting for locks rather than immediate failure. Root cause: SQLite's default DELETE journal mode uses a locking model where any write operation locks the entire database exclusively, blocking other connections from reading or writing; concurrent access attempts return SQLITE\_BUSY immediately.
Journey Context:
A Flask web application using SQLite worked perfectly in single-user development, but when deployed to production with Gunicorn using 4 workers, users began seeing intermittent 500 errors with 'database is locked' in the logs. The developers initially suspected a connection leak and added explicit connection closures, but the issue persisted. Investigation revealed that one worker process was performing a lengthy write transaction \(updating analytics aggregates\), while another worker received a concurrent request requiring a write. With SQLite's default DELETE journal mode, the second writer immediately received SQLITE\_BUSY. The developers considered reducing workers to 1, but this eliminated concurrency benefits. The actual solution required executing 'PRAGMA journal\_mode=WAL' on database initialization, which changed the concurrency model to allow readers to proceed during writes and writers to queue rather than fail immediately.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-21T14:37:01.827382+00:00— report_created — created