diff --git a/plugins/memory/holographic/store.py b/plugins/memory/holographic/store.py index 34c857d0a0..802ffaf49f 100644 --- a/plugins/memory/holographic/store.py +++ b/plugins/memory/holographic/store.py @@ -671,7 +671,14 @@ class MemoryStore: try: entry["conn"].close() finally: - MemoryStore._shared.pop(self._key, None) + # Pop only OUR entry. After release_all_under() force- + # closed this entry (profile delete, #88347) a same-path + # store may have re-registered a FRESH entry under the + # same key; a stale holder's late close() must not evict + # it — that would silently reintroduce the multi-writer + # contention this registry exists to prevent. + if MemoryStore._shared.get(self._key) is entry: + MemoryStore._shared.pop(self._key, None) self._entry = None def __enter__(self) -> "MemoryStore": diff --git a/tests/plugins/memory/test_holographic_shutdown_closes_db.py b/tests/plugins/memory/test_holographic_shutdown_closes_db.py index 436d0d2ceb..1058e7fd77 100644 --- a/tests/plugins/memory/test_holographic_shutdown_closes_db.py +++ b/tests/plugins/memory/test_holographic_shutdown_closes_db.py @@ -91,3 +91,40 @@ def test_release_all_under_closes_connections_inside_directory_only(tmp_path): outside.close() + + +def test_stale_holder_close_does_not_evict_fresh_registry_entry(tmp_path): + """Follow-up to #88347 — a stale holder's late ``close()`` must be inert. + + After ``release_all_under`` force-closes a profile's connection, a store + re-created on the same path registers a FRESH shared entry under the same + key. If the stale holder (whose entry was force-closed) then calls + ``close()``, it must not pop the fresh entry out of the registry — that + would let a third store open a SECOND connection to the same database and + silently reintroduce the multi-writer contention the registry prevents. + """ + from plugins.memory.holographic.store import MemoryStore + + profile_dir = tmp_path / "profiles" / "default-2" + profile_dir.mkdir(parents=True) + db_path = profile_dir / "memory_store.db" + + stale = MemoryStore(db_path=db_path, hrr_dim=64) + assert MemoryStore.release_all_under(profile_dir) == 1 + + fresh = MemoryStore(db_path=db_path, hrr_dim=64) + key = fresh._key + fresh_entry = MemoryStore._shared[key] + + # The stale holder's late close must leave the fresh entry registered... + stale.close() + assert MemoryStore._shared.get(key) is fresh_entry + # ...and a third store must attach to the SAME shared connection. + third = MemoryStore(db_path=db_path, hrr_dim=64) + try: + assert third._conn is fresh._conn + finally: + third.close() + fresh.close() + # Normal last-holder close still evicts its own entry. + assert key not in MemoryStore._shared