fix(gateway): keep stale route when recovery lookup fails

This commit is contained in:
dsad 2026-07-12 18:42:34 +03:00 committed by Teknium
parent f5b6112226
commit d17daf0b12
2 changed files with 28 additions and 0 deletions

View file

@ -1193,12 +1193,14 @@ class SessionStore:
# end_reason not None -> session ended — prune
if row is not None and row.get("end_reason") is not None:
recovered_entry = None
recovery_lookup_failed = False
if entry.origin is not None:
try:
recovered_entry = self._recover_session_from_db(
session_key=key,
source=entry.origin,
now=_now(),
raise_on_lookup_error=True,
)
except Exception as exc:
logger.debug(
@ -1208,6 +1210,10 @@ class SessionStore:
entry.session_id,
exc,
)
recovery_lookup_failed = True
if recovery_lookup_failed:
continue
# If the stale entry points at a compression-ended parent but
# a newer live child session exists for the exact same gateway
@ -1431,6 +1437,7 @@ class SessionStore:
session_key: str,
source: SessionSource,
now: datetime,
raise_on_lookup_error: bool = False,
) -> Optional[SessionEntry]:
"""Rebuild a missing session-key mapping from durable state.db data."""
if not self._db:
@ -1449,6 +1456,8 @@ class SessionStore:
)
except Exception as exc:
logger.debug("Gateway session DB recovery failed for %s: %s", session_key, exc)
if raise_on_lookup_error:
raise
return None
if not recovered:
return None

View file

@ -151,6 +151,25 @@ class TestPruneStaleSessionsLocked:
assert key not in store._entries
def test_keeps_stale_entry_when_recovery_lookup_raises(self, tmp_path):
"""Indeterminate recovery must not delete the only routing handle.
Startup pruning sees an ended parent and tries to repoint it to the
latest live gateway child. If that recovery query raises, deleting the
sessions.json entry loses the routing key entirely; keeping it lets the
runtime stale guard retry recovery on the next message.
"""
key = "agent:main:telegram:dm:5140768830"
db = _db_returning({"sid_parent": {"end_reason": "compression", "id": "sid_parent"}})
db.find_latest_gateway_session_for_peer.side_effect = RuntimeError("db busy")
store = _make_store_with_db(tmp_path, db)
store._entries[key] = _make_entry_with_origin(key, "sid_parent")
store._prune_stale_sessions_locked()
assert key in store._entries
assert store._entries[key].session_id == "sid_parent"
def test_noop_when_db_is_none(self, tmp_path):
config = GatewayConfig(default_reset_policy=SessionResetPolicy(mode="none"))
with patch("gateway.session.SessionStore._ensure_loaded"):