Skip to content

fix: ManagedMemoryStore sync methods must respect the path prefix - #258

Closed
d-v-b wants to merge 6 commits into
mainfrom
fix/store-sync-async-parity
Closed

fix: ManagedMemoryStore sync methods must respect the path prefix#258
d-v-b wants to merge 6 commits into
mainfrom
fix/store-sync-async-parity

Conversation

@d-v-b

@d-v-b d-v-b commented Jul 29, 2026

Copy link
Copy Markdown
Owner

🤖 AI text below 🤖

Fixes the third critical from the PR zarr-developers#3885 audit, and adds the store-suite contract laws that would have caught it.

The bug: ManagedMemoryStore overrides every async method to prefix keys with self.path, but inherited get_sync/set_sync/delete_sync from MemoryStore, which use raw keys. The inherited methods satisfy the sync protocols, so FusedCodecPipeline took the sync fast path and wrote chunks outside the store's namespace: with path="subdir", metadata landed at subdir/zarr.json but chunks at c/0; re-reading through a fresh handle silently returned fill values. Two stores sharing a dict with different paths cross-talked. The three sync methods now mirror the async overrides via _join_paths.

Bonus bug found by the new fixture: ManagedMemoryStore.get_partial_values double-applied the prefix (the base implementation dispatches through the already-prefixing self.get), so it returned None for every key whenever path was non-empty. Fixed by removing the redundant override.

Why tests missed both: TestManagedMemoryStore used an empty path and its raw set/get helpers also skipped the prefix — the prefix machinery was never exercised. The fixture now uses a non-empty path and the helpers respect it.

Structural fix — sync/async contract laws in StoreTests, inherited by every store subclass (protocol-based skips for non-sync stores): async-set→get_sync and set_sync→async-get parity on the same instance; delete_sync visibility to async get; get_sync byte_range parity (Range/offset/suffix/past-EOF — previously untested on any store). Plus: GpuMemoryStore.set_sync now performs the same cpu→gpu conversion as async set (gpu-marked test), a LocalStore.delete_sync directory-branch test, and an end-to-end fused-pipeline regression asserting no keys escape the prefix.

15 new tests fail pre-fix (TDD); full suite 6831 passed / 404 skipped / 4 xfailed; both audit repro scripts run clean.

🤖 Generated with Claude Code

ManagedMemoryStore inherited get_sync/set_sync/delete_sync from
MemoryStore, which use the raw key, while every async method prefixed
keys with self.path. Any code taking the sync fast path (e.g.
FusedCodecPipeline) wrote/read chunks outside the store's path prefix,
so a fresh handle re-reading through the prefix silently got fill
values. Override the three sync methods to prefix like their async
counterparts.

GpuMemoryStore.set_sync gets the same treatment: it now converts its
value to a gpu.Buffer like set does, preserving the store's
all-values-are-gpu invariant for the sync API.

Also fix ManagedMemoryStore.get_partial_values, which applied self.path
twice whenever path was non-empty (it pre-prefixed keys, then
delegated to MemoryStore.get_partial_values, which itself dispatches
through the already-overridden self.get) -- this made it return None
for every key. Discovered via the strengthened test fixture below.

Add sync/async parity laws to the shared StoreTests suite so every
store subclass exercises this invariant: set through one API and read
through the other (including byte_range variants), and confirm
delete_sync is visible to async get. These are the tests that would
have caught the ManagedMemoryStore bug.

TestManagedMemoryStore's raw set/get test helpers now respect self.path,
and store_kwargs uses a non-empty path, so prefix handling is actually
exercised instead of passing vacuously. Add an end-to-end regression
with FusedCodecPipeline writing to a ManagedMemoryStore(path=...)
sharing a dict with a fresh handle. Add a LocalStore.delete_sync
directory-branch test.

Assisted-by: ClaudeCode:claude-sonnet-5
@d-v-b
d-v-b force-pushed the fix/store-sync-async-parity branch from 78228b1 to 1a20c4c Compare July 29, 2026 14:35
@d-v-b d-v-b closed this Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants