fix(gateway): resume sessions after crash/restart instead of blanket suspend
suspend_recently_active() was unconditionally setting suspended=True on startup, causing get_or_create_session() to wipe conversation history on every restart. Change to set resume_pending=True instead, so sessions auto-resume while still allowing stuck-loop escalation after 3 failures.
This commit is contained in:
@@ -2755,7 +2755,7 @@ class GatewayRunner:
|
||||
try:
|
||||
suspended = self.session_store.suspend_recently_active()
|
||||
if suspended:
|
||||
logger.info("Suspended %d in-flight session(s) from previous run", suspended)
|
||||
logger.info("Marked %d in-flight session(s) as resumable from previous run", suspended)
|
||||
except Exception as e:
|
||||
logger.warning("Session suspension on startup failed: %s", e)
|
||||
|
||||
|
||||
@@ -1086,19 +1086,22 @@ class SessionStore:
|
||||
return len(removed_keys)
|
||||
|
||||
def suspend_recently_active(self, max_age_seconds: int = 120) -> int:
|
||||
"""Mark recently-active sessions as suspended.
|
||||
"""Mark recently-active sessions as resumable after an unexpected exit.
|
||||
|
||||
Called on gateway startup to prevent sessions that were likely
|
||||
in-flight when the gateway last exited from being blindly resumed
|
||||
(#7536). Only suspends sessions updated within *max_age_seconds*
|
||||
to avoid resetting long-idle sessions that are harmless to resume.
|
||||
Returns the number of sessions that were suspended.
|
||||
Called on gateway startup after a crash or fast restart to preserve
|
||||
in-flight sessions instead of destroying their conversation history
|
||||
(#7536). Only marks sessions updated within *max_age_seconds* to
|
||||
avoid touching long-idle sessions. Sets ``resume_pending=True`` so
|
||||
the next incoming message on the same session_key auto-resumes from
|
||||
the existing transcript.
|
||||
|
||||
Entries flagged ``resume_pending=True`` are skipped — those were
|
||||
marked intentionally by the drain-timeout path as recoverable.
|
||||
Terminal escalation for genuinely stuck ``resume_pending`` sessions
|
||||
is handled by the existing ``.restart_failure_counts`` stuck-loop
|
||||
counter, which runs after this method on startup.
|
||||
Entries already flagged ``resume_pending=True`` are skipped. Entries
|
||||
explicitly ``suspended=True`` (from /stop or stuck-loop escalation)
|
||||
are also skipped. Terminal escalation for genuinely stuck sessions
|
||||
is still handled by the existing ``.restart_failure_counts`` counter
|
||||
(threshold 3), which runs after this method and sets ``suspended=True``.
|
||||
|
||||
Returns the number of sessions marked resumable.
|
||||
"""
|
||||
from datetime import timedelta
|
||||
|
||||
@@ -1110,7 +1113,9 @@ class SessionStore:
|
||||
if entry.resume_pending:
|
||||
continue
|
||||
if not entry.suspended and entry.updated_at >= cutoff:
|
||||
entry.suspended = True
|
||||
entry.resume_pending = True
|
||||
entry.resume_reason = "restart_interrupted"
|
||||
entry.last_resume_marked_at = _now()
|
||||
count += 1
|
||||
if count:
|
||||
self._save()
|
||||
|
||||
Reference in New Issue
Block a user