From f36a4278725a1f03cb95ab2bb43c44f6dac53773 Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Fri, 26 Jun 2026 10:19:26 -0400 Subject: [PATCH 1/2] fix(models): make session-index rebuild thread ownership explicit (#3894) --- api/models.py | 3 ++- tests/test_session_index.py | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/api/models.py b/api/models.py index 485a1a42c77..c8fa14bb8e3 100644 --- a/api/models.py +++ b/api/models.py @@ -200,6 +200,7 @@ def _session_dir_has_persisted_session_files() -> bool: def _rebuild_session_index_background(expected_session_dir: Path, expected_index_file: Path) -> None: global _SESSION_INDEX_REBUILD_THREAD, _SESSION_INDEX_REBUILD_THREAD_TARGET + current_thread = threading.current_thread() try: with _SESSION_INDEX_REBUILD_LOCK: if SESSION_DIR != expected_session_dir or SESSION_INDEX_FILE != expected_index_file: @@ -213,7 +214,7 @@ def _rebuild_session_index_background(expected_session_dir: Path, expected_index logger.debug("Background session-index rebuild failed", exc_info=True) finally: with _SESSION_INDEX_REBUILD_LOCK: - if _SESSION_INDEX_REBUILD_THREAD_TARGET == ( + if _SESSION_INDEX_REBUILD_THREAD is current_thread and _SESSION_INDEX_REBUILD_THREAD_TARGET == ( expected_session_dir, expected_index_file, ): diff --git a/tests/test_session_index.py b/tests/test_session_index.py index 9ef7acef6f6..9530d8e2c4d 100644 --- a/tests/test_session_index.py +++ b/tests/test_session_index.py @@ -1666,3 +1666,30 @@ def _switch_globals_then_write(*args, **kwargs): assert not new_index_file.exists() rows = _read_index(original_index_file) assert [row["session_id"] for row in rows] == ["late_switch_sid"] + + +def test_background_rebuild_old_thread_finally_preserves_new_same_target_owner(tmp_path, monkeypatch): + session_dir = tmp_path / "sessions" + session_dir.mkdir(exist_ok=True) + index_file = session_dir / "_index.json" + target = (session_dir, index_file) + + monkeypatch.setattr(models, "SESSION_DIR", session_dir) + monkeypatch.setattr(models, "SESSION_INDEX_FILE", index_file) + + old_thread = object() + new_thread = object() + monkeypatch.setattr(models, "_SESSION_INDEX_REBUILD_THREAD", old_thread) + monkeypatch.setattr(models, "_SESSION_INDEX_REBUILD_THREAD_TARGET", target) + monkeypatch.setattr(models.threading, "current_thread", lambda: old_thread) + + def _handoff_then_write(*args, **kwargs): + monkeypatch.setattr(models, "_SESSION_INDEX_REBUILD_THREAD", new_thread) + monkeypatch.setattr(models, "_SESSION_INDEX_REBUILD_THREAD_TARGET", target) + + monkeypatch.setattr(models, "_write_session_index", _handoff_then_write) + + models._rebuild_session_index_background(*target) + + assert models._SESSION_INDEX_REBUILD_THREAD is new_thread + assert models._SESSION_INDEX_REBUILD_THREAD_TARGET == target From 877cdac3c66675cf0c8adcf641eab71af649dfdf Mon Sep 17 00:00:00 2001 From: nesquena-hermes Date: Fri, 26 Jun 2026 15:25:13 +0000 Subject: [PATCH 2/2] Release YJ (v0.51.680): explicit session-index rebuild thread ownership (#4993, #3894) --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67896db1771..d048426b0f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ ## [Unreleased] +## [v0.51.680] — 2026-06-26 — Release YJ (session-index rebuild can't be clobbered by a late worker) + +### Fixed + +- **A late-finishing background session-index rebuild can no longer clobber a newer rebuild's state.** When a session-index rebuild worker finished, it cleared the rebuild bookkeeping globals if only the target tuple matched — so an older worker completing after a newer rebuild had already taken over could wipe the newer owner's registration. The cleanup now also requires that the finishing worker is still the registered owner thread (`_SESSION_INDEX_REBUILD_THREAD is current_thread`, checked under the rebuild lock), so an out-of-order older worker leaves the newer owner's state intact while the genuine owner still clears normally. Thanks @rodboev. (#4993, #3894) + ## [v0.51.679] — 2026-06-26 — Release YI (faster fresh sidebar boot — parallel session/project fetches) ### Fixed