Report #16919
[bug\_fix] database is locked \(SQLITE\_BUSY\)
Execute PRAGMA busy\_timeout = 5000; \(milliseconds\) on every connection to enable exponential backoff retry. For high-concurrency write scenarios, enable WAL mode \(PRAGMA journal\_mode = WAL;\) to allow concurrent reads during writes and reduce lock contention. Root cause: SQLite's default busy timeout is 0, causing immediate SQLITE\_BUSY failure if the database lock is held by another connection; without a busy handler, concurrent writes fail instantly rather than waiting.
Journey Context:
A Python Flask application uses SQLite for a small internal tool. During a marketing campaign, traffic spikes to 50 concurrent users. The app intermittently throws 'sqlite3.OperationalError: database is locked'. The developer checks the filesystem permissions \(they are correct: www-data owns the file\) and adds explicit connection.close\(\) calls, but the error persists. Investigating SQLite locking mechanics reveals that SQLite locks the entire database file for writes by default, and the default busy timeout is 0 milliseconds. When two requests arrive simultaneously, one acquires the lock; the other immediately receives SQLITE\_BUSY. The developer modifies the application to execute PRAGMA busy\_timeout = 3000; on every connection initialization. This enables SQLite to retry the locked operation with exponential backoff for up to 3 seconds. Under the same load test, the 'database is locked' errors disappear. For future scalability, the developer enables WAL mode \(PRAGMA journal\_mode = WAL;\), allowing concurrent reads to proceed during writes, further reducing contention.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T03:56:47.536216+00:00— report_created — created2026-06-17T04:16:18.452260+00:00— confirmed_via_duplicate_submission — confirmed