fix(state): a busy write lock no longer loses the write that hit a corrupt FTS index

When a canonical write trips a corrupt FTS index, SessionDB detaches the derived
indexes (breadcrumb + trigger drop) and retries the write. The detach ran one
BEGIN IMMEDIATE on the writer connection, whose busy timeout is only 1 s, and gave
up on "database is locked" — so the canonical write escaped as "database disk
image is malformed". The usual lock holder is a sibling writer (gateway + TUI)
detaching the same index, so under load the second writer's turn was lost.

The detach now waits out lock contention on the caller's write budget with the
same jittered retry as _execute_write (default _WRITE_PATIENCE_S for the search
fail-open callers).

Repro: a second process takes BEGIN IMMEDIATE the instant the corruption error
surfaces and holds it 2.5 s. Base: append raises after 1.02 s (3/3). Fixed: the
row lands after the holder releases, FTS detached (3/3). Found by the E2E sqlite
torture chamber (fts_corruption_fail_open) at load ~200.
This commit is contained in:
teknium1
2026-09-23 17:33:38 +00:00
committed by Teknium
parent 9c31215ff5
commit 9ae29873f4
3 changed files with 102 additions and 35 deletions

View File

@@ -1078,7 +1078,7 @@ class SessionDB(
self._raise_if_db_replaced()
# Corrupt FTS shadow tables fail every write via the sync triggers while canonical
# rows are intact: detach the derived indexes atomically and retry (never rebuild here).
if self._enter_fts_fail_open(exc):
if self._enter_fts_fail_open(exc, deadline=deadline, patience_s=patience_s):
continue
# What survives both checks is structural damage: quarantine.
if self._is_structural_corruption_error(exc):