Report #88148
[bug\_fix] database is locked \(SQLITE\_BUSY\)
Enable WAL mode \(PRAGMA journal\_mode=WAL\) to allow readers and writers to proceed concurrently, and set a busy timeout \(PRAGMA busy\_timeout=milliseconds\) so connections wait for locks rather than returning SQLITE\_BUSY immediately. Root cause: SQLite's legacy DELETE journal mode uses database-level locking where a writer obtains a RESERVED lock that blocks other writers, and readers block writers; without WAL, lock contention immediately returns errors.
Journey Context:
You deploy a Python Flask application using SQLite as the database, running under Gunicorn with 4 worker processes. Under moderate load, users intermittently receive \`sqlite3.OperationalError: database is locked\`. You check SQLite documentation and realize the default journal\_mode is DELETE, which uses exclusive locking: when one worker writes, it obtains a RESERVED lock, and other writers immediately get SQLITE\_BUSY. You try adding \`timeout=30000\` to the connection string, but still get errors because the default mode doesn't support concurrent readers and writers efficiently. The rabbit hole leads you to Write-Ahead Logging \(WAL\) mode. You execute \`PRAGMA journal\_mode=WAL;\` on the database. This changes the semantics: readers read from the main database file while writers append to the \`-wal\` file, so readers don't block writers and writers don't block readers. However, you still occasionally see locks when two writers collide. You add \`PRAGMA busy\_timeout=5000;\` \(5 seconds\) so that if a writer encounters a locked database, it sleeps and retries rather than returning immediately. The combination of WAL mode \(separating read/write concerns\) and busy timeout \(graceful contention handling\) eliminates the "database is locked" errors entirely.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T06:32:33.457829+00:00— report_created — created