Report #5227
[bug\_fix] database is locked \(SQLITE\_BUSY\)
Set a busy timeout using PRAGMA busy\_timeout = 5000 \(milliseconds\) to instruct SQLite to retry the operation with exponential backoff rather than returning SQLITE\_BUSY immediately, and refactor the application to ensure write transactions are as short as possible to minimize lock contention.
Journey Context:
An Android fitness tracking app stores workout data locally in SQLite. Users report sporadic "database is locked" crashes when ending a workout while the app simultaneously performs a background sync to the cloud. The developer reproduces the issue by starting a manual data export \(which holds a write transaction for 30 seconds\) and then attempting to save a new workout. The default SQLite configuration on Android has a busy timeout of 0 \(milliseconds\), meaning it returns SQLITE\_BUSY immediately if the database is locked, causing the crash. The developer initially enables WAL mode \(PRAGMA journal\_mode=WAL\), which improves concurrency by allowing readers to proceed during writes, but write operations still block each other, so the error persists when two write transactions conflict. The correct fix is to set a busy timeout. The developer modifies the SQLiteOpenHelper to execute PRAGMA busy\_timeout = 10000 \(10 seconds\) immediately after opening the database. Now, when the main thread tries to write while the background sync holds the lock, SQLite retries the operation with exponential backoff for up to 10 seconds instead of failing immediately. The main thread briefly waits \(typically less than 100ms for the background sync to commit\) and then succeeds. Additionally, the developer refactors the background sync to commit in smaller batches \(100 rows per transaction\) rather than one large transaction, further reducing lock contention.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T20:52:39.660292+00:00— report_created — created