Fix StateManager deadlock with explicit BEGIN IMMEDIATE + retry
with conn: context manager in Python sqlite3 doesn't respect busy_timeout for the implicit BEGIN; under concurrent subprocess writes the transaction would fail immediately. Replace with explicit BEGIN IMMEDIATE + retry loop (6 attempts, 0.5s back-off) to handle WAL write contention.
This commit is contained in:
+21
-4
@@ -250,12 +250,29 @@ class StateManager:
|
|||||||
return
|
return
|
||||||
|
|
||||||
conn = self._get_write_conn()
|
conn = self._get_write_conn()
|
||||||
try:
|
for attempt in range(6):
|
||||||
with conn:
|
try:
|
||||||
|
conn.execute("BEGIN IMMEDIATE")
|
||||||
for sql, params in batch:
|
for sql, params in batch:
|
||||||
conn.execute(sql, params)
|
conn.execute(sql, params)
|
||||||
except Exception:
|
conn.execute("COMMIT")
|
||||||
logger.exception("StateManager flush failed (%d ops)", len(batch))
|
return
|
||||||
|
except sqlite3.OperationalError as e:
|
||||||
|
try:
|
||||||
|
conn.execute("ROLLBACK")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if attempt < 5:
|
||||||
|
time.sleep(0.5 * (attempt + 1))
|
||||||
|
else:
|
||||||
|
logger.error("StateManager flush failed after 6 attempts (%d ops): %s", len(batch), e)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
conn.execute("ROLLBACK")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
logger.exception("StateManager flush failed (%d ops)", len(batch))
|
||||||
|
return
|
||||||
|
|
||||||
def _writer_loop(self) -> None:
|
def _writer_loop(self) -> None:
|
||||||
"""Background loop: flush write queue every FLUSH_INTERVAL seconds."""
|
"""Background loop: flush write queue every FLUSH_INTERVAL seconds."""
|
||||||
|
|||||||
Reference in New Issue
Block a user