Report #52816
[bug\_fix] SQLite database is locked \(SQLITE\_BUSY\) under concurrent writes
By default, SQLite uses rollback journal mode \(DELETE or TRUNCATE\) which allows only one writer at a time with exclusive locks. When multiple threads or processes attempt to write concurrently, subsequent writers receive SQLITE\_BUSY \('database is locked'\). The definitive fix is to enable WAL \(Write-Ahead Logging\) mode by executing PRAGMA journal\_mode=WAL; on the database. WAL mode allows readers to proceed without blocking writers and writers do not block readers \(though only one writer at a time still\). Additionally, set a busy timeout handler \(PRAGMA busy\_timeout=5000;\) so that if a writer encounters a locked database, it waits up to 5000ms instead of failing immediately. Ensure all connections share the same WAL mode setting.
Journey Context:
You deploy a small Flask internal tool using SQLite to a production server with gunicorn running 4 workers. It works fine with one user, but when three people submit forms simultaneously, two get 'sqlite3.OperationalError: database is locked' 500 errors. You check the SQLite docs and learn the default journal\_mode is DELETE, which serializes writes with exclusive locks. You connect to the DB manually and run PRAGMA journal\_mode; which returns 'delete'. You execute PRAGMA journal\_mode=WAL; and verify with PRAGMA journal\_mode; showing 'wal'. You also modify your connection initialization to execute PRAGMA busy\_timeout=3000; so it waits briefly if locked. After deploying, concurrent writes now queue briefly instead of failing, read performance improves because readers don't block on the writer's exclusive lock, and the 'database is locked' errors disappear entirely.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T19:08:48.462506+00:00— report_created — created