Skip to content

Fix 'database is locked' fatal when running all scrapers - #2278

Open
moToroTor wants to merge 1 commit into
xbapps:masterfrom
moToroTor:fix/2139-database-locked
Open

moToroTor wants to merge 1 commit into
xbapps:masterfrom
moToroTor:fix/2139-database-locked

Conversation

@moToroTor

@moToroTor moToroTor commented Sep 25, 2026 •

Copy link
Copy Markdown

Fixes #2139.

Running all selected scrapers dies with Failed to save ... database is locked (10 retries) and the process exits. Root cause, all in pkg/models/db.go:

  • SQLite opens with no pragmas: delete journal mode + the driver's 5s busy timeout. In delete mode every read locks against the writer, and with dozens of concurrent scrapers (default concurrent_scrapers is 9999) writer queues routinely exceed 5s.
  • SaveWithRetry retried 10x at ~100ms, then log.Fatal killed the whole server. --concurrent_scrapers 1 cannot help: it only batches scraper goroutines, it never serialized DB writes.

Changes:

  • New sqliteWriteDSN() appends _busy_timeout=30000&_journal_mode=WAL&_synchronous=NORMAL for the sqlite3 driver only (MySQL untouched), used by both GetDB() and GetCommonDB(). DSN params apply to every pooled connection. WAL stops readers blocking the writer; 30s covers writer queues.
  • SaveWithRetry: 30 attempts with 1s-15s backoff. The log.Fatal backstop is kept deliberately: every call site ignores the error return, so returning it would turn loud death into silently skipped saves. Transient locks no longer reach it.
  • New pkg/models/db_sqlite_test.go: DSN unit test, WAL/timeout assertions, and a lock-contention regression test (8s write hold; fails at ~5s on the plain DSN with the reported error, waits and succeeds with the fix).

Notes: existing databases flip to WAL on first open after upgrade (backward compatible, SQLite-recommended pairing with synchronous=NORMAL). No changes to scan, edit, or scrapers.

…s#2139)

SQLite opened with no pragmas: journal_mode=delete plus the driver's 5s
busy_timeout. With all scrapers selected, dozens of concurrent writers and
UI polling reads pile up past 5s, SaveWithRetry's 10x100ms all fail, and
log.Fatal kills the server. Set _busy_timeout=30000&_journal_mode=WAL&
_synchronous=NORMAL on every sqlite handle via the DSN (sqlite3 driver
only) and back off SaveWithRetry to 30 attempts, 1s-15s. Fatal backstop
kept: call sites ignore the error return, so returning it would silently
skip saves.

Assisted-By: muse-spark-1.3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

database is locked

1 participant