Fix SQLite database locking under concurrent access

Add busy_timeout (5 seconds) to both async and sync database connections.
This prevents "database is locked" errors when multiple requests try to
access the database simultaneously.

Changes:
- AsyncSQLitePool: Add busy_timeout=5000 and synchronous=NORMAL pragmas
- DatabaseCache: Add WAL mode, busy_timeout, synchronous=NORMAL, and
  connection timeout for the sync database module

The busy_timeout tells SQLite to wait instead of immediately failing
when the database is locked by another connection.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rob 2026-03-06 13:40:58 -04:00
parent bf95490673
commit c4bcc19241
2 changed files with 12 additions and 1 deletions

View File

@ -76,6 +76,11 @@ class AsyncSQLitePool:
conn = await aiosqlite.connect(self.db_path) conn = await aiosqlite.connect(self.db_path)
# Enable WAL mode for better concurrent read/write performance # Enable WAL mode for better concurrent read/write performance
await conn.execute("PRAGMA journal_mode=WAL") await conn.execute("PRAGMA journal_mode=WAL")
# Set busy timeout to wait up to 5 seconds when database is locked
# This prevents "database is locked" errors during concurrent access
await conn.execute("PRAGMA busy_timeout=5000")
# Use NORMAL synchronous mode with WAL for good performance + durability
await conn.execute("PRAGMA synchronous=NORMAL")
# Enable foreign keys # Enable foreign keys
await conn.execute("PRAGMA foreign_keys=ON") await conn.execute("PRAGMA foreign_keys=ON")
return conn return conn

View File

@ -73,8 +73,14 @@ class DatabaseCache:
@contextmanager @contextmanager
def _get_connection(self): def _get_connection(self):
"""Get a database connection with proper cleanup.""" """Get a database connection with proper cleanup."""
conn = sqlite3.connect(self.db_path) conn = sqlite3.connect(self.db_path, timeout=5.0)
conn.row_factory = sqlite3.Row conn.row_factory = sqlite3.Row
# Enable WAL mode for better concurrent read/write performance
conn.execute("PRAGMA journal_mode=WAL")
# Set busy timeout to wait when database is locked (in addition to connect timeout)
conn.execute("PRAGMA busy_timeout=5000")
# Use NORMAL synchronous mode with WAL for good performance + durability
conn.execute("PRAGMA synchronous=NORMAL")
try: try:
yield conn yield conn
finally: finally: