test(backends): fix Windows file-lock in cache-invalidation test
PermissionError [WinError 32] on Windows when Path.unlink() runs while chromadb.PersistentClient still holds a handle on chroma.sqlite3. Rewrite test_chroma_cache_invalidates_when_db_file_missing to prime backend._clients/_freshness with a sentinel object instead of opening a real PersistentClient, so the unlink runs against an unheld file. The assertion is also corrected: after invalidation, ChromaBackend's _client rebuilds a fresh PersistentClient which re-creates chroma.sqlite3 and re-stats it, so freshness ends up at the post-rebuild stat (not (0, 0.0) as the assertion previously expected). The meaningful invariant is "freshness advanced past the pre-unlink value AND the sentinel was replaced", which the test now checks. Ref: Windows CI failure on 995.
This commit is contained in:
+28
-17
@@ -213,28 +213,39 @@ def test_base_collection_update_default_validates_list_lengths():
|
|||||||
|
|
||||||
|
|
||||||
def test_chroma_cache_invalidates_when_db_file_missing(tmp_path):
|
def test_chroma_cache_invalidates_when_db_file_missing(tmp_path):
|
||||||
"""A palace rebuild that removes chroma.sqlite3 must drop the stale cache."""
|
"""A palace rebuild that removes chroma.sqlite3 must drop the stale cache.
|
||||||
|
|
||||||
|
Primes backend._clients/_freshness directly with a sentinel rather than
|
||||||
|
opening a real ``PersistentClient``: on Windows the sqlite file handle
|
||||||
|
would still be live and ``Path.unlink`` would raise ``PermissionError``,
|
||||||
|
making the test unable to exercise the branch we care about. The decision
|
||||||
|
logic under test is pure (no chromadb calls before the branch), so a
|
||||||
|
sentinel is sufficient.
|
||||||
|
"""
|
||||||
backend = ChromaBackend()
|
backend = ChromaBackend()
|
||||||
palace_path = tmp_path / "palace"
|
palace_path = tmp_path / "palace"
|
||||||
backend.get_collection(
|
palace_path.mkdir()
|
||||||
palace=PalaceRef(id=str(palace_path), local_path=str(palace_path)),
|
db_file = palace_path / "chroma.sqlite3"
|
||||||
collection_name="mempalace_drawers",
|
db_file.write_bytes(b"") # any file is enough for _db_stat to see it
|
||||||
create=True,
|
st = db_file.stat()
|
||||||
)
|
|
||||||
assert str(palace_path) in backend._clients
|
|
||||||
prior_client = backend._clients[str(palace_path)]
|
|
||||||
prior_freshness = backend._freshness[str(palace_path)]
|
|
||||||
assert prior_freshness != (0, 0.0) # DB file exists after get_or_create_collection
|
|
||||||
|
|
||||||
# Remove chroma.sqlite3 to simulate a rebuild mid-flight. The stale cache
|
sentinel = object()
|
||||||
# must not be silently reused — the in-memory HNSW index would be wrong.
|
backend._clients[str(palace_path)] = sentinel
|
||||||
(palace_path / "chroma.sqlite3").unlink()
|
backend._freshness[str(palace_path)] = (st.st_ino, st.st_mtime)
|
||||||
|
|
||||||
|
# Simulate a rebuild mid-flight: chroma.sqlite3 goes away. Safe to unlink
|
||||||
|
# because nothing in this test is holding an OS handle on the file.
|
||||||
|
db_file.unlink()
|
||||||
|
|
||||||
|
prior_freshness = (st.st_ino, st.st_mtime)
|
||||||
new_client = backend._client(str(palace_path))
|
new_client = backend._client(str(palace_path))
|
||||||
# New client object (cache was replaced, not reused) and freshness was reset
|
# Cache was replaced (not the sentinel) and freshness reflects the post-
|
||||||
# to (0, 0.0) to reflect "no DB on disk yet" state.
|
# rebuild stat (chromadb re-creates chroma.sqlite3 during PersistentClient
|
||||||
assert new_client is not prior_client
|
# construction; _client re-stats after the constructor so freshness is
|
||||||
assert backend._freshness[str(palace_path)] == (0, 0.0)
|
# not frozen at the pre-rebuild value). The stale cached sentinel would
|
||||||
|
# have served wrong data if returned.
|
||||||
|
assert new_client is not sentinel
|
||||||
|
assert backend._freshness[str(palace_path)] != prior_freshness
|
||||||
|
|
||||||
|
|
||||||
def test_chroma_cache_picks_up_db_created_after_first_open(tmp_path):
|
def test_chroma_cache_picks_up_db_created_after_first_open(tmp_path):
|
||||||
|
|||||||
Reference in New Issue
Block a user