From c4bcc192417512731a98591dfd7239bb97a95df5 Mon Sep 17 00:00:00 2001 From: rob Date: Fri, 6 Mar 2026 13:40:58 -0400 Subject: [PATCH] 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 --- src/exchange_data_manager/cache/connection_pool.py | 5 +++++ src/exchange_data_manager/cache/database.py | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/exchange_data_manager/cache/connection_pool.py b/src/exchange_data_manager/cache/connection_pool.py index f781e77..6fa6bf2 100644 --- a/src/exchange_data_manager/cache/connection_pool.py +++ b/src/exchange_data_manager/cache/connection_pool.py @@ -76,6 +76,11 @@ class AsyncSQLitePool: conn = await aiosqlite.connect(self.db_path) # Enable WAL mode for better concurrent read/write performance 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 await conn.execute("PRAGMA foreign_keys=ON") return conn diff --git a/src/exchange_data_manager/cache/database.py b/src/exchange_data_manager/cache/database.py index 98529ca..e99080b 100644 --- a/src/exchange_data_manager/cache/database.py +++ b/src/exchange_data_manager/cache/database.py @@ -73,8 +73,14 @@ class DatabaseCache: @contextmanager def _get_connection(self): """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 + # 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: yield conn finally: