From 095557677cda1bee5f368bb9134f72d8fcc0aaac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Aug 2026 17:55:45 +0000 Subject: [PATCH 1/4] Initial plan From 27c274ed92085e22c10ac1af238abbbc0286228a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Aug 2026 18:00:36 +0000 Subject: [PATCH 2/4] Fix EmbedStore.__getitem__ race condition by holding lock during read Co-authored-by: FrancescAlted <314521+FrancescAlted@users.noreply.github.com> --- src/blosc2/embed_store.py | 23 ++++++++++++----------- tests/test_locking.py | 26 ++++++-------------------- 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/blosc2/embed_store.py b/src/blosc2/embed_store.py index 7ac77c54b..22e48439e 100644 --- a/src/blosc2/embed_store.py +++ b/src/blosc2/embed_store.py @@ -288,17 +288,18 @@ def __setitem__( def __getitem__(self, key: str) -> blosc2.NDArray | SChunk | blosc2.ObjectArray | blosc2.BatchArray: """Retrieve a node from the embed store.""" - self._sync_metadata() - if key not in self._embed_map: - raise KeyError(f"Key '{key}' not found in the embed store.") - node_info = self._embed_map[key] - urlbase = node_info.get("urlbase", None) - if urlbase: - urlpath = blosc2.URLPath(node_info["path"], urlbase=urlbase) - return blosc2.open(urlpath, mode="r") - offset = node_info["offset"] - length = node_info["length"] - serialized_data = bytes(self._store[offset : offset + length]) + with self._backing_schunk.holding_lock(): + self._sync_metadata() + if key not in self._embed_map: + raise KeyError(f"Key '{key}' not found in the embed store.") + node_info = self._embed_map[key] + urlbase = node_info.get("urlbase", None) + if urlbase: + urlpath = blosc2.URLPath(node_info["path"], urlbase=urlbase) + return blosc2.open(urlpath, mode="r") + offset = node_info["offset"] + length = node_info["length"] + serialized_data = bytes(self._store[offset : offset + length]) # It is safer to copy data here, as the reference to the SChunk may disappear # Use from_cframe so we can deserialize either an NDArray or an SChunk return blosc2.from_cframe(serialized_data, copy=True) diff --git a/tests/test_locking.py b/tests/test_locking.py index 608faf925..106b9642e 100644 --- a/tests/test_locking.py +++ b/tests/test_locking.py @@ -1176,19 +1176,10 @@ def test_embed_store_cross_process_writers(tmp_path): keys = list(estore) assert "/seed" in keys for key in keys[-3:]: - try: - node = estore.get(key) # a concurrent delete cannot happen here - if node is None: - continue - data = node[:] - except RuntimeError: - # Same "listed before it is readable" window the None check - # covers, deeper in: the index already carries the key's - # (offset, length) but the backing schunk has not grown to - # cover it, so EmbedStore.__getitem__ raises "Error while - # getting the slice". Tolerated only while the writers are - # running -- every key is verified strictly once they exit. + node = estore.get(key) # a concurrent delete cannot happen here + if node is None: continue + data = node[:] assert len(data) == 10 nreads += 1 finally: @@ -1277,15 +1268,10 @@ def test_dict_store_cross_process_writers(tmp_path): for key in keys: if not key.endswith("ext3"): continue - try: - node = dstore.get(key) - if node is None: - continue - data = node[:] - except RuntimeError: - # See test_embed_store_cross_process_writers: the index - # carries the key before its bytes are readable. + node = dstore.get(key) + if node is None: continue + data = node[:] assert len(data) == 100 nreads += 1 finally: From b45b2b90edb43fed6cdd846453e92d6926f4401a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Aug 2026 18:10:58 +0000 Subject: [PATCH 3/4] Move C2Array open outside the exclusive lock in EmbedStore.__getitem__ Co-authored-by: FrancescAlted <314521+FrancescAlted@users.noreply.github.com> --- src/blosc2/embed_store.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/blosc2/embed_store.py b/src/blosc2/embed_store.py index 22e48439e..f7864b4a0 100644 --- a/src/blosc2/embed_store.py +++ b/src/blosc2/embed_store.py @@ -294,12 +294,14 @@ def __getitem__(self, key: str) -> blosc2.NDArray | SChunk | blosc2.ObjectArray raise KeyError(f"Key '{key}' not found in the embed store.") node_info = self._embed_map[key] urlbase = node_info.get("urlbase", None) - if urlbase: - urlpath = blosc2.URLPath(node_info["path"], urlbase=urlbase) - return blosc2.open(urlpath, mode="r") - offset = node_info["offset"] - length = node_info["length"] - serialized_data = bytes(self._store[offset : offset + length]) + if not urlbase: + offset = node_info["offset"] + length = node_info["length"] + serialized_data = bytes(self._store[offset : offset + length]) + + if urlbase: + # Outside the lock: opening a C2Array involves an HTTP round trip + return blosc2.open(blosc2.URLPath(node_info["path"], urlbase=urlbase), mode="r") # It is safer to copy data here, as the reference to the SChunk may disappear # Use from_cframe so we can deserialize either an NDArray or an SChunk return blosc2.from_cframe(serialized_data, copy=True) From de453f7a1bede9edb45766503bb4847047800fda Mon Sep 17 00:00:00 2001 From: Francesc Alted Date: Wed, 5 Aug 2026 20:26:57 +0200 Subject: [PATCH 4/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/blosc2/embed_store.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/blosc2/embed_store.py b/src/blosc2/embed_store.py index f7864b4a0..d3c69180e 100644 --- a/src/blosc2/embed_store.py +++ b/src/blosc2/embed_store.py @@ -288,7 +288,18 @@ def __setitem__( def __getitem__(self, key: str) -> blosc2.NDArray | SChunk | blosc2.ObjectArray | blosc2.BatchArray: """Retrieve a node from the embed store.""" - with self._backing_schunk.holding_lock(): + if self._shared: + with self._backing_schunk.holding_lock(): + self._sync_metadata() + if key not in self._embed_map: + raise KeyError(f"Key '{key}' not found in the embed store.") + node_info = self._embed_map[key] + urlbase = node_info.get("urlbase", None) + if not urlbase: + offset = node_info["offset"] + length = node_info["length"] + serialized_data = bytes(self._store[offset : offset + length]) + else: self._sync_metadata() if key not in self._embed_map: raise KeyError(f"Key '{key}' not found in the embed store.") @@ -298,7 +309,6 @@ def __getitem__(self, key: str) -> blosc2.NDArray | SChunk | blosc2.ObjectArray offset = node_info["offset"] length = node_info["length"] serialized_data = bytes(self._store[offset : offset + length]) - if urlbase: # Outside the lock: opening a C2Array involves an HTTP round trip return blosc2.open(blosc2.URLPath(node_info["path"], urlbase=urlbase), mode="r")