perf(sessions): prime missing index in background
This commit is contained in:
1 parent
4ea762ae0d
commit
e265389116
3 files changed
+76
No files matched your search
@@ -3,6 +3,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- When the session index is missing, WebUI now starts a background rebuild while preserving the first sidebar full-scan result, so the index is primed for later requests without temporarily hiding existing sessions.
|
||||
|
||||
## [v0.51.134] — 2026-05-25 — Release DF (stage-batch16 — single-PR Windows path defaults)
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -51,6 +51,8 @@ _STALE_TMP_AGE_SECONDS = 3600 # 1 hour
|
||||
# Serializes index writers so concurrent Session.save() calls cannot race on
|
||||
# stale baselines while still allowing LOCK to be released before disk I/O.
|
||||
_INDEX_WRITE_LOCK = threading.RLock()
|
||||
_SESSION_INDEX_REBUILD_LOCK = threading.Lock()
|
||||
_SESSION_INDEX_REBUILD_THREAD = None
|
||||
|
||||
|
||||
def _cleanup_stale_tmp_files() -> None:
|
||||
@@ -74,6 +76,32 @@ def _cleanup_stale_tmp_files() -> None:
|
||||
pass # SESSION_DIR may not exist yet; that's fine
|
||||
|
||||
|
||||
def _rebuild_session_index_background() -> None:
|
||||
try:
|
||||
_write_session_index(updates=None)
|
||||
except Exception:
|
||||
logger.debug("Background session-index rebuild failed", exc_info=True)
|
||||
|
||||
|
||||
def _start_session_index_rebuild_thread() -> None:
|
||||
"""Start one background full-index rebuild if the index is missing."""
|
||||
global _SESSION_INDEX_REBUILD_THREAD
|
||||
with _SESSION_INDEX_REBUILD_LOCK:
|
||||
if SESSION_INDEX_FILE.exists():
|
||||
return
|
||||
if (
|
||||
_SESSION_INDEX_REBUILD_THREAD is not None
|
||||
and _SESSION_INDEX_REBUILD_THREAD.is_alive()
|
||||
):
|
||||
return
|
||||
_SESSION_INDEX_REBUILD_THREAD = threading.Thread(
|
||||
target=_rebuild_session_index_background,
|
||||
name="session-index-rebuild",
|
||||
daemon=True,
|
||||
)
|
||||
_SESSION_INDEX_REBUILD_THREAD.start()
|
||||
|
||||
|
||||
def _index_entry_exists(session_id: str, in_memory_ids=None) -> bool:
|
||||
"""Return True if an index entry still has backing state.
|
||||
|
||||
@@ -2236,6 +2264,9 @@ def all_sessions(diag=None):
|
||||
active_stream_ids = _active_stream_ids()
|
||||
# Phase C: try index first for O(1) read; fall back to full scan
|
||||
_diag_stage(diag, "all_sessions.index_exists")
|
||||
if not SESSION_INDEX_FILE.exists():
|
||||
_diag_stage(diag, "all_sessions.start_index_rebuild")
|
||||
_start_session_index_rebuild_thread()
|
||||
if SESSION_INDEX_FILE.exists():
|
||||
try:
|
||||
_diag_stage(diag, "all_sessions.read_index")
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
"""Regression tests for #2863 missing session-index background rebuild."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import time
|
||||
|
||||
|
||||
def test_missing_index_starts_background_rebuild_while_preserving_first_scan(monkeypatch, tmp_path):
|
||||
import api.models as models
|
||||
from api.models import all_sessions
|
||||
|
||||
session_dir = tmp_path / "sessions"
|
||||
session_dir.mkdir(parents=True)
|
||||
monkeypatch.setattr(models, "SESSION_DIR", session_dir)
|
||||
monkeypatch.setattr(models, "SESSION_INDEX_FILE", session_dir / "_index.json")
|
||||
models.SESSIONS.clear()
|
||||
|
||||
for idx in range(3):
|
||||
payload = {
|
||||
"session_id": f"issue2863{idx}",
|
||||
"title": f"Session {idx}",
|
||||
"workspace": str(tmp_path),
|
||||
"model": "test-model",
|
||||
"messages": [{"role": "user", "content": f"hello {idx}", "timestamp": time.time() + idx}],
|
||||
"created_at": time.time() + idx,
|
||||
"updated_at": time.time() + idx,
|
||||
}
|
||||
(session_dir / f"issue2863{idx}.json").write_text(json.dumps(payload), encoding="utf-8")
|
||||
|
||||
rows = all_sessions()
|
||||
|
||||
assert {row["session_id"] for row in rows} == {"issue28630", "issue28631", "issue28632"}
|
||||
|
||||
thread = models._SESSION_INDEX_REBUILD_THREAD
|
||||
assert thread is not None
|
||||
thread.join(timeout=5)
|
||||
assert not thread.is_alive()
|
||||
|
||||
index = json.loads(models.SESSION_INDEX_FILE.read_text(encoding="utf-8"))
|
||||
assert {row["session_id"] for row in index} == {"issue28630", "issue28631", "issue28632"}
|
||||
assert {row["session_id"] for row in all_sessions()} == {"issue28630", "issue28631", "issue28632"}
|
||||
Reference in new issue
Block a user