Report #4915
[bug\_fix] database is locked \(SQLITE\_BUSY\)
Execute PRAGMA busy\_timeout = 5000 \(milliseconds\) immediately after opening each connection to allow SQLite to retry acquiring the lock instead of returning SQLITE\_BUSY immediately. Ensure all connections share the same journal mode \(PRAGMA journal\_mode=WAL\), avoid placing the database on network filesystems \(NFS\), and ensure no connection holds a read transaction open indefinitely \(which blocks WAL checkpoints\).
Journey Context:
A Python Flask app using SQLite in WAL mode works perfectly in development with a single process. Deployed to production with Gunicorn \(4 workers\), users intermittently see 'database is locked' errors during writes. The developer realizes SQLite's default busy\_timeout is 0, meaning it returns SQLITE\_BUSY immediately if the database is locked. They attempt adding PRAGMA busy\_timeout=30000 but still see errors during high load. Investigating with lsof and strace, they discover one worker is keeping a read transaction open \(an uncommitted SELECT statement\) for the duration of a long-running external API call. This read transaction blocks the checkpoint operation in the WAL, causing write operations to fail with BUSY even with busy\_timeout set. The final fix combines setting a reasonable busy\_timeout with refactoring to ensure transactions \(especially reads\) are committed or rolled back promptly, and ensuring the database directory is on a local filesystem, not NFS.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T20:17:46.153392+00:00— report_created — created