Release v0.51.329 — Release KS (#3814 O(n) index map + #3815 startup recovery skip) (#3840)
Some checks failed
Release & Docker / release (push) Has been cancelled
Some checks failed
Release & Docker / release (push) Has been cancelled
#3814: O(n) index-count map for /api/sessions. #3815: skip backup-less sidecars on startup recovery. Full suite 8275, Codex SAFE, Opus SHIP, CI 11/11. Co-authored-by: ai-ag2026 <ai-ag2026@users.noreply.github.com>
This commit is contained in:
@@ -513,24 +513,41 @@ def _read_metadata_json_prefix(path, max_prefix_bytes=65536):
|
||||
|
||||
def _lookup_index_message_count(session_id):
|
||||
"""Return the indexed message count without loading the full session file."""
|
||||
try:
|
||||
entries = json.loads(SESSION_INDEX_FILE.read_text(encoding='utf-8'))
|
||||
except Exception:
|
||||
return None
|
||||
return _index_message_count_map().get(str(session_id))
|
||||
|
||||
|
||||
def _index_message_count_map(entries=None) -> dict[str, int]:
|
||||
"""Return indexed message counts keyed by session id.
|
||||
|
||||
``load_metadata_only()`` is called in loops for stale lineage/sidebar rows.
|
||||
Reading and parsing ``_index.json`` once per row turns /api/sessions into an
|
||||
accidental O(n²) poll for old sidecars that predate persisted
|
||||
``message_count``. Accepting already-loaded index rows lets callers reuse
|
||||
the index they just parsed.
|
||||
"""
|
||||
if entries is None:
|
||||
try:
|
||||
entries = json.loads(SESSION_INDEX_FILE.read_text(encoding='utf-8'))
|
||||
except Exception:
|
||||
return {}
|
||||
if not isinstance(entries, list):
|
||||
return None
|
||||
return {}
|
||||
counts: dict[str, int] = {}
|
||||
for entry in entries:
|
||||
if entry.get('session_id') != session_id:
|
||||
if not isinstance(entry, dict):
|
||||
continue
|
||||
sid = str(entry.get('session_id') or '')
|
||||
if not sid:
|
||||
continue
|
||||
count = entry.get('message_count')
|
||||
if isinstance(count, int) and count >= 0:
|
||||
return count
|
||||
try:
|
||||
count = int(count)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
return count if count >= 0 else None
|
||||
return None
|
||||
if not isinstance(count, int):
|
||||
try:
|
||||
count = int(count)
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if count >= 0:
|
||||
counts[sid] = count
|
||||
return counts
|
||||
|
||||
|
||||
def _parse_nonnegative_int(value):
|
||||
@@ -791,7 +808,7 @@ class Session:
|
||||
return session
|
||||
|
||||
@classmethod
|
||||
def load_metadata_only(cls, sid):
|
||||
def load_metadata_only(cls, sid, *, index_message_counts=None):
|
||||
"""Load only the compact metadata fields, skipping the messages array.
|
||||
|
||||
Session JSON files have metadata fields (session_id, title, model, etc.)
|
||||
@@ -820,7 +837,10 @@ class Session:
|
||||
sidecar_message_count = _parse_nonnegative_int(parsed.get('message_count'))
|
||||
index_message_count = None
|
||||
if sidecar_message_count is None:
|
||||
index_message_count = _lookup_index_message_count(sid)
|
||||
if index_message_counts is not None:
|
||||
index_message_count = index_message_counts.get(str(sid))
|
||||
else:
|
||||
index_message_count = _lookup_index_message_count(sid)
|
||||
# Modern sidecars carry an accurate message_count, so it is the
|
||||
# source of truth and we skip the per-row _index.json read in the
|
||||
# common case. The sidebar index is only a cache (it can lag behind
|
||||
@@ -2728,7 +2748,11 @@ def _stale_snapshot_metadata_refresh_ids(sessions: list[dict]) -> set[str]:
|
||||
return refresh_ids
|
||||
|
||||
|
||||
def _refresh_index_rows_from_sidecar_metadata(sessions: list[dict]) -> list[dict]:
|
||||
def _refresh_index_rows_from_sidecar_metadata(
|
||||
sessions: list[dict],
|
||||
*,
|
||||
index_message_counts: dict[str, int] | None = None,
|
||||
) -> list[dict]:
|
||||
"""Overlay fuller sidecar metadata onto stale sidebar index rows.
|
||||
|
||||
``_index.json`` is a cache and can lag behind the canonical session sidecar
|
||||
@@ -2749,7 +2773,10 @@ def _refresh_index_rows_from_sidecar_metadata(sessions: list[dict]) -> list[dict
|
||||
if not sid:
|
||||
out.append(session)
|
||||
continue
|
||||
sidecar = Session.load_metadata_only(sid)
|
||||
sidecar = Session.load_metadata_only(
|
||||
sid,
|
||||
index_message_counts=index_message_counts,
|
||||
)
|
||||
if not sidecar:
|
||||
out.append(session)
|
||||
continue
|
||||
@@ -3051,7 +3078,11 @@ def all_sessions(diag=None):
|
||||
active_stream_ids=active_stream_ids,
|
||||
)
|
||||
_diag_stage(diag, "all_sessions.refresh_sidecar_metadata")
|
||||
refreshed_index_rows = _refresh_index_rows_from_sidecar_metadata(list(index_map.values()))
|
||||
index_message_counts = _index_message_count_map(index)
|
||||
refreshed_index_rows = _refresh_index_rows_from_sidecar_metadata(
|
||||
list(index_map.values()),
|
||||
index_message_counts=index_message_counts,
|
||||
)
|
||||
index_map = {
|
||||
row['session_id']: row
|
||||
for row in refreshed_index_rows
|
||||
|
||||
Reference in New Issue
Block a user