mirror of
https://github.com/calibrain/shelfmark.git
synced 2026-11-14 17:28:25 +00:00
The users, download history and activity view-state databases open their connections with SQLite's default busy timeout of 5 seconds. A start-up migration, the restart reconcile, or any statement that overlaps a backup or a second process raised "database is locked" after 5 seconds, and the reconcile finished only part of its work. One shared `DB_BUSY_TIMEOUT_SECONDS` (30) in `core/utils.py`, used by all three. The test reads SQLite's `busy_timeout` pragma back from each connection, so it doesn't need a real lock fight. I left `UserDB.initialize()` without a guard on purpose. With 30 seconds the wait covers a backup, and swallowing a schema failure there would hide a worker that can't run. Found in a Claude pass over 1.5.0.
28 lines
928 B
Python
28 lines
928 B
Python
"""The app databases wait out a busy writer instead of failing at SQLite's 5 s default."""
|
|
|
|
import pytest
|
|
|
|
from shelfmark.core.activity_view_state_service import ActivityViewStateService
|
|
from shelfmark.core.download_history_service import DownloadHistoryService
|
|
from shelfmark.core.user_db import UserDB
|
|
|
|
# SQLite's own default is 5000 ms. A start-up migration or a restart reconcile that meets a
|
|
# writer for a few seconds should wait, not raise.
|
|
_MIN_BUSY_TIMEOUT_MS = 15_000
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"service_cls",
|
|
[UserDB, DownloadHistoryService, ActivityViewStateService],
|
|
)
|
|
def test_app_database_connections_wait_for_a_busy_writer(tmp_path, service_cls):
|
|
service = service_cls(str(tmp_path / "users.db"))
|
|
|
|
conn = service._connect()
|
|
try:
|
|
busy_timeout_ms = conn.execute("PRAGMA busy_timeout").fetchone()[0]
|
|
finally:
|
|
conn.close()
|
|
|
|
assert busy_timeout_ms >= _MIN_BUSY_TIMEOUT_MS
|