Skip to content

Commit ce3a22c

Browse files
committed
Getting ready for release 4.8.1
1 parent 8a1d774 commit ce3a22c

4 files changed

Lines changed: 93 additions & 50 deletions

File tree

ANNOUNCE.rst

Lines changed: 35 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,43 @@
1-
Announcing Python-Blosc2 4.8.0
1+
Announcing Python-Blosc2 4.8.1
22
==============================
33

4-
We are happy to announce this release, which brings support for
5-
**sharing containers safely across processes** — opt-in file locking for
6-
``SChunk``/``NDArray``/``EmbedStore``/``DictStore``, atomic archive
7-
replacement, and readers that follow another process's writes — plus a
8-
couple of data-loss and data-correctness bug fixes worth upgrading for even
9-
if you don't touch any of the new locking API.
4+
This is a maintenance release with a solid batch of bug fixes — including
5+
use-after-free hazards around zero-copy cframes, wrong-chunk deletion with
6+
negative-step slices, and inconsistent ``DictStore`` overwrite semantics —
7+
plus read-only memory mapping for ``CTable`` stores and a documentation
8+
restructuring with a new Optimization Tips section.
109

1110
The main highlights are:
1211

13-
- **Cross-process locking**: a new ``locking`` storage parameter (and the
14-
``BLOSC_LOCKING`` env var to enable it fleet-wide) serializes accesses to an
15-
on-disk ``SChunk``/``NDArray``/``EmbedStore``/``DictStore`` against other
16-
handles and processes via a small sidecar lock file. ``holding_lock()``
17-
holds the exclusive lock across several operations and now auto-refreshes
18-
the handle right after acquiring it, so a decision made inside the block
19-
never acts on a stale in-memory read.
20-
21-
- **Cross-process writes for ``EmbedStore``/``DictStore``** (``.b2d``): under
22-
locking, one process can add or remove keys while another has the store
23-
open, and the other side's next lookup sees the change — no need to
24-
reopen the store.
25-
26-
- **Atomic ``.b2z`` archives**: writing a ``DictStore.to_b2z()`` file (which
27-
``TreeStore`` also uses) now swaps the new file in atomically. A process
28-
reading the archive concurrently always gets either the complete old
29-
version or the complete new one — never a partially-written file from a
30-
save that's still in progress. This needs no locking on the reader's side.
31-
32-
- **Growth-SWMR**: a reader ``NDArray`` handle opened before a ``resize()``
33-
made through another handle follows the new shape on its next data access,
34-
or via the new explicit ``NDArray.refresh()`` / ``SChunk.refresh()``.
35-
36-
- **Two bug fixes worth knowing about**: ``NDArray.append()`` could silently
37-
delete another writer's just-appended data under concurrent growth (fixed
38-
by refreshing the cached shape before computing the resize target); and
39-
``detect_aligned_chunks()`` could silently return the wrong chunk's data
40-
for an aligned slice on arrays whose shape isn't a multiple of the chunk
41-
shape (a floor-division bug that undercounted the chunk grid).
42-
43-
- New user guide page,
44-
`Sharing containers across processes
45-
<https://www.blosc.org/python-blosc2/guides/sharing_across_processes.html>`_,
46-
covering all of the above plus the caveats (NFS, ``mmap_mode``, Windows
47-
in-use-file rename).
48-
49-
A quick taste — hold the lock across a read-modify-write from two processes::
50-
51-
with ndarr.holding_lock():
52-
ndarr[:] = ndarr[:] + 1 # atomic w.r.t. other locked handles
12+
- **Read-only mmap for ``CTable`` stores**: ``CTable.open()`` gains an
13+
``mmap_mode="r"`` parameter, mirroring ``blosc2.open()``. All members of a
14+
read-only store — scalar, list, varlen and dictionary columns alike — are
15+
read from mapped pages; for ``.b2z`` archives, in place inside the single
16+
mapped container file. With several concurrent readers on one file this
17+
pays off quickly: 2.5x/4.4x/4.5x faster wall time for 1/4/8 readers in
18+
our benchmark.
19+
20+
- **Zero-copy cframe fix**: ``schunk_from_cframe()`` /
21+
``ndarray_from_cframe()`` with ``copy=False`` (the default) returned
22+
objects pointing into the caller's bytes buffer without keeping it alive,
23+
so a temporary cframe could be reclaimed under the live object, corrupting
24+
reads. The buffer is now pinned on the returned object.
25+
26+
- **More correctness fixes**: negative-step slice deletion in
27+
``BatchArray``/``ObjectArray`` removed the wrong chunks; ``DictStore``
28+
overwrite semantics depended on value size (now uniformly dict-like);
29+
``stack()``/``vecdot()`` shape inference was off for negative axes;
30+
chunked ``matmul()`` mishandled broadcast batch dims; and
31+
``ListArray.extend_arrow()`` could reorder unflushed rows.
32+
33+
- **Faster ``.b2z``/``.b2d`` opens**: a builtin-shadowing bug made store
34+
detection in ``blosc2.open()`` silently recurse ~250 times on every open.
35+
36+
- **Docs restructuring**, with a new `Optimization tips
37+
<https://www.blosc.org/python-blosc2/guides/optimization_tips.html>`_
38+
section, including tips on grouping related data into a single
39+
memory-mapped ``.b2z`` file and on using ``mmap_mode="r"`` with many
40+
concurrent readers.
5341

5442
Install it with::
5543

RELEASE_NOTES.md

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,62 @@
22

33
## Changes from 4.8.0 to 4.8.1
44

5-
XXX version-specific blurb XXX
5+
### Improvements
6+
7+
- Read-only memory mapping for `CTable` stores: `CTable.open()` (and
8+
`FileTableStorage`) gain an `mmap_mode="r"` parameter, mirroring
9+
`blosc2.open()`. All members of a read-only store — scalar, list, varlen
10+
and dictionary columns alike — are then read from mapped pages; for `.b2z`
11+
archives, in place at their offsets inside the single mapped container
12+
file. With several concurrent readers on one file this pays off quickly:
13+
2.5x/4.4x/4.5x faster wall time for 1/4/8 readers in our benchmark
14+
(`bench/optim_tips/tip_10_mmap_many_readers.py`).
15+
- Reduced memory consumption in `CTable.extend()` when passed an `NDArray`.
16+
17+
### Bug fixes
18+
19+
- Fixed lifetime/use-after-free hazards around zero-copy cframes and
20+
`vlmeta`: `schunk_from_cframe()`/`ndarray_from_cframe()` with `copy=False`
21+
(the default) returned objects pointing into the caller's bytes buffer
22+
without keeping it alive, so a temporary cframe (e.g.
23+
`ndarray_from_cframe(response.content)`) could be reclaimed under the live
24+
object, corrupting reads. The buffer is now pinned on the returned object.
25+
Also, `vlmeta` read paths (`__getitem__`/`__len__`/`__iter__`) now raise
26+
`ReferenceError` on an orphaned owner instead of segfaulting, matching the
27+
write paths.
28+
- `BatchArray.delete()` / `ObjectArray.delete()`: negative-step slices
29+
(e.g. `del arr[3:0:-1]`) deleted chunks in ascending order, shifting the
30+
indices of chunks still to be deleted and removing the wrong ones (or
31+
raising `RuntimeError`).
32+
- `ListArray.extend_arrow()`: Arrow chunks were appended to the backend
33+
without flushing pending cells first, reordering unflushed rows after the
34+
new ones.
35+
- Shape inference: `stack()` with a negative axis inserted the new dimension
36+
one position too early, so a lazyexpr's reported `.shape` disagreed with
37+
its computed result; `vecdot()` also normalized positive axes as if they
38+
were negative.
39+
- Chunked `matmul()`: broadcast (size-1) operand batch dims were sliced with
40+
the result-chunk coordinates, producing empty slices when the broadcast
41+
dim spans several result chunks.
42+
- `DictStore.__setitem__()`: overwrite semantics depended on value size —
43+
embedded keys refused overwrite ("already exists"), while an
44+
embedded-to-external overwrite double-stored the key and resurrected the
45+
stale embedded value after a delete. Assignments now behave uniformly
46+
dict-like, dropping any previous value.
47+
- Explicitly passing `cparams=None` or `dparams=None` to NDArray
48+
constructors crashed; both now mean "defaults".
49+
- Fixed a builtin-shadowing bug that made store detection in
50+
`blosc2.open()` recurse ~250 times (silently swallowed) on every
51+
`.b2z`/`.b2d` open; opening a `.b2z` is now ~10x faster under allocation
52+
tracing.
53+
54+
### Documentation
55+
56+
- Restructured docs (#674), with a new
57+
[Optimization tips](https://www.blosc.org/python-blosc2/guides/optimization_tips.html)
58+
section, including new tips on grouping related data into a single
59+
memory-mapped `.b2z` file and on using `mmap_mode="r"` with many
60+
concurrent readers.
661

762
## Changes from 4.7.0 to 4.8.0
863

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ dependencies = [
4141
"rich",
4242
"threadpoolctl; platform_machine != 'wasm32'",
4343
]
44-
version = "4.8.1.dev0"
44+
version = "4.8.1"
4545
[project.entry-points."array_api"]
4646
blosc2 = "blosc2"
4747

src/blosc2/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version__ = "4.8.1.dev0"
1+
__version__ = "4.8.1"
22
__array_api_version__ = "2024.12"

0 commit comments

Comments
 (0)