Report #27091
[bug\_fix] WAL file grows unbounded / checkpoint starvation \(SQLite\)
Terminate long-running read transactions \(analytics queries\), or configure a smaller auto-checkpoint threshold \(PRAGMA wal\_autocheckpoint=100\), or explicitly run PRAGMA wal\_checkpoint\(TRUNCATE\) during maintenance windows. For read-heavy analytics, use a separate replica. Root cause: SQLite checkpoints require exclusive lock; long-running readers block checkpoint, preventing WAL truncation, causing append-only growth.
Journey Context:
A Node.js analytics platform uses better-sqlite3 for high-throughput event logging. They enable WAL mode for write performance. After several days of operation, the disk fills up despite the main database file being only 2GB. Investigation reveals a \`.sqlite-wal\` file that has grown to 80GB. They attempt to manually checkpoint using \`PRAGMA wal\_checkpoint\(TRUNCATE\)\` via a SQL client, but the command hangs indefinitely or returns SQLITE\_BUSY. They check for active connections and find a long-running analytics query that was started 18 hours ago by a data scientist connecting directly to the database. In WAL mode, a checkpoint cannot truncate the WAL file past the point of the oldest active read transaction. Because this long-running reader holds back the checkpoint, all new writes append to the WAL file indefinitely, causing unbounded growth. The immediate fix is to kill the long-running query or disconnect the client, allowing the checkpoint to proceed and truncate the WAL. To prevent recurrence, they reduce the auto-checkpoint threshold from 1000 to 100 pages \(PRAGMA wal\_autocheckpoint=100\), and they establish a policy that long-running analytics queries must use a separate read-only replica or export to a data warehouse, not the production SQLite database. They also implement monitoring alerts for WAL file size.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T23:52:16.817202+00:00— report_created — created