fix(state): add REINDEX strategy to repair stale B-tree indexes (#63386)

When PRAGMA integrity_check reports 'wrong # of entries in index' for
B-tree indexes (e.g. idx_sessions_handoff_state), the existing repair
strategies (FTS rebuild, sqlite_master dedup, drop-FTS+VACUUM) don't
address the mismatch. Add Strategy 0.5: run REINDEX to rewrite the
index b-tree from canonical table rows before escalating to more
destructive strategies.
This commit is contained in:
liuhao1024
2026-07-13 04:38:39 +08:00
committed by Teknium
parent 373ec23e37
commit 505fb58751
2 changed files with 52 additions and 0 deletions
+23
View File
@@ -748,6 +748,29 @@ def repair_state_db_schema(db_path: Path, *, backup: bool = True) -> Dict[str, A
except sqlite3.DatabaseError as exc:
logger.warning("state.db FTS in-place rebuild pass failed: %s", exc)
# ── Strategy 0.5: rebuild stale B-tree indexes (#63386) ──
# PRAGMA integrity_check can report "wrong # of entries in index" when a
# B-tree index (e.g. idx_sessions_handoff_state) falls out of sync with its
# base table. REINDEX rewrites the index b-tree from the canonical table
# rows using the existing index definition, fixing the mismatch without
# touching data or FTS schema.
try:
conn = sqlite3.connect(str(db_path), isolation_level=None)
try:
conn.execute("REINDEX")
conn.commit()
finally:
conn.close()
if _db_opens_cleanly(db_path) is None:
report["repaired"] = True
report["strategy"] = "reindex_btree"
logger.warning(
"state.db B-tree indexes rebuilt via REINDEX: %s", db_path
)
return report
except sqlite3.DatabaseError as exc:
logger.warning("state.db REINDEX pass failed: %s", exc)
# ── Strategy 1: de-duplicate sqlite_master (keeps FTS index) ──
try:
conn = sqlite3.connect(str(db_path), isolation_level=None)
+29
View File
@@ -487,6 +487,35 @@ def test_repair_noop_db_uses_already_healthy_shortcut(tmp_path):
assert report["strategy"] == "already_healthy"
def test_repair_rebuilds_stale_btree_indexes(tmp_path, monkeypatch):
"""repair_state_db_schema uses REINDEX for 'wrong # of entries in index'.
When PRAGMA integrity_check reports a stale B-tree index (e.g.
idx_sessions_handoff_state), the FTS-rebuild and dedup strategies don't
help — REINDEX rewrites the index b-tree from the canonical table rows.
"""
db_path = tmp_path / "state.db"
_build_healthy_db(db_path)
_reason = "wrong # of entries in index idx_sessions_handoff_state"
_call_count = {"n": 0}
_real_check = hermes_state._db_opens_cleanly
def _simulated_check(path):
_call_count["n"] += 1
# initial health check + post-Strategy-0 check report corruption;
# after REINDEX (Strategy 0.5) the DB is healthy.
if _call_count["n"] <= 2:
return _reason
return _real_check(path)
monkeypatch.setattr(hermes_state, "_db_opens_cleanly", _simulated_check)
report = repair_state_db_schema(db_path)
assert report["repaired"] is True
assert report["strategy"] == "reindex_btree"
def test_select_cached_agent_history_prefers_longer_live_transcript():
"""Gateway guard keeps the live transcript when persisted history lags."""
from gateway.run import _select_cached_agent_history