fix(eu-umbau): SQLite-Parallelitaet der Doppelspur abgesichert
busy_timeout von 5s auf 30s (zwei gleichzeitig schreibende Lanes) und 'database is locked' im Orchestrator als transienter Fehler mit Retry statt als permanenter Abbruch. Gefunden im Doppelspur-Paralleltest (Lage-Summary-Update kollidierte mit der Schreiblast der zweiten Lane). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -616,6 +616,13 @@ class AgentOrchestrator:
|
||||
def _is_transient_cli(err: Exception) -> bool:
|
||||
return isinstance(err, ClaudeCliError) and err.error_type in ("rate_limit", "timeout")
|
||||
|
||||
def _is_db_locked(err: Exception) -> bool:
|
||||
# Seit der Doppelspur (Phase 2) koennen sich parallele Lanes beim
|
||||
# Schreiben auf die SQLite-DB kurz blockieren. Das ist transient
|
||||
# und einen Retry wert, kein permanenter Fehler.
|
||||
import sqlite3 as _sqlite3
|
||||
return isinstance(err, _sqlite3.OperationalError) and "locked" in str(err).lower()
|
||||
|
||||
# Optionales globales Ventil (Default aus): begrenzt gleichzeitige Recherchen.
|
||||
sem = self._global_sem
|
||||
if sem is not None:
|
||||
@@ -657,7 +664,7 @@ class AgentOrchestrator:
|
||||
break
|
||||
|
||||
# Transiente Fehler: Retry bis 3x
|
||||
if isinstance(e, TRANSIENT_ERRORS) or _is_transient_cli(e):
|
||||
if isinstance(e, TRANSIENT_ERRORS) or _is_transient_cli(e) or _is_db_locked(e):
|
||||
last_error = e
|
||||
kind = e.error_type if isinstance(e, ClaudeCliError) else type(e).__name__
|
||||
logger.warning(f"Transienter Fehler [{kind}] bei Lage {incident_id} (Versuch {attempt + 1}/3): {e}")
|
||||
|
||||
@@ -392,7 +392,10 @@ async def get_db() -> aiosqlite.Connection:
|
||||
db.row_factory = aiosqlite.Row
|
||||
await db.execute("PRAGMA journal_mode=WAL")
|
||||
await db.execute("PRAGMA foreign_keys=ON")
|
||||
await db.execute("PRAGMA busy_timeout=5000")
|
||||
# 30s statt 5s seit der Doppelspur (EU-Umbau Phase 2). Zwei gleichzeitig
|
||||
# laufende Refreshes derselben Organisation schreiben parallel in die DB,
|
||||
# 5s reichten dabei nicht immer (database is locked beim Summary-Update).
|
||||
await db.execute("PRAGMA busy_timeout=30000")
|
||||
return db
|
||||
|
||||
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren