Repository navigation
v0.16.0
Breaking
The packed-suffix cargo feature is retired (#1599). Naming it now fails the build with a compile_error!, so remove it from your feature list. In every build, string-map suffix leaves are now allocated from the map's own node allocator. The diagnostic ablation-unpacked-suffix feature restores the old global allocation.
Python 3.8 and 3.9 are no longer supported (#1584).
- Wheels are
cp310-abi3(they werecp38-abi3), andrequires-python = ">=3.10", so pip on 3.8 or 3.9 keeps resolvingexpanse-trie0.15.x. - Reason: GHSA-6w46-j5rx-g56g (pytest < 9.0.3) is fixed only in pytest 9.0.3, which needs Python 3.10.
Changed (string maps)
A memory reading that no longer stops writers (#1599). On 64-bit builds, SyncExpanseStrMap::mem_used, the ordered-bytes map's delegate and expanse_sync_strmap_mem_used in C read the allocator's counters without excluding writers.
- Accuracy: the value is exact once writes finish, and can differ by the bytes of the operations in flight while writers run. It is not an upper bound.
mem_heldstill excludes writers.- Stall removed: with 4 writers at 2·10⁷ keys and a reader polling once per second, the largest insert went from 0.5–2 s to about 0.15 ms (
docs/benchmarks/concurrency/METHODOLOGY.md§47.10).
What mem_used counts changed on every ExpanseStrMap, plain or shared.
- Node shells and suffix leaves are now allocator blocks, counted at their allocated sizes.
mem_heldon prefix-heavy keys rises, because allocator overhead is now counted.- Uuid-shaped keys use 17–20% less memory, allocator headers included (§47.2).
- Budgets set from earlier readings should be re-derived.
Cost (METHODOLOGY §47.10–§47.13):
- Shared string inserts lose about 5% throughput at 1 writer and 3–4% at 8.
- Instruction counts rise by up to +4.66% on shared short-key inserts.
- Plain
strmap_churnis +1.15% on CI, a cost accepted after one remedy failed its bound (#1604).
Fixed
stats()on a string map filled by concurrent writers (#1605). It now sums tomem_used. Before, it stopped at the first branch whose count those writers had left stale, and reported a fraction of the map.- A single insert stalling for most of 30 s (#1606). This happened on a
SyncExpanseStrMapheld at constant population under short-key churn. Every removal that took the serialised path recounted the whole sub-map it removed from, with writers held. It now counts only as far as its decision needs,ROOT_LEAF_CAP + 1keys. On the reference host the cell's largest insert reads BCa 95% [96,050, 422,553] ns (METHODOLOGY §48.4).
Known limitations
- Serialised removals at large N. Under short-key churn at 2·10⁷ keys, a large share of removals still take the serialised path, which limits multi-writer throughput there. The cause is not yet measured (#1610).
- Outdated string-map figures. The string-map memory and throughput figures in
README.mdand thehot_comparisonandmasstree_comparisonREADMEs predate this release and are marked pending re-measurement (#1611).
Compatibility
- Public Rust API: unchanged (
.github/public-api/diffv0.15.0..mainis empty). include/Judy.h: unchanged.include/expanse.h: comments only. They state themem_usedandmem_heldsemantics above for the shared string, bytes and ordered-bytes maps.- Binary image format: unchanged (version 2).
Dependencies
What's Changed
Other Changes
- ci(python): drop Python 3.8 support, floor moves to 3.10 by @orieg in #1584
- bench(strmap): Callgrind arm for SyncExpanseStrMap::mem_used, the locked walk (Refs #1586) by @orieg in #1587
- chore(deps): webpki-root-certs 1.0.10 for RUSTSEC-2026-0383 by @orieg in #1590
- bench(strmap): stall of a periodic SyncExpanseStrMap::mem_used reading, METHODOLOGY §44 (Refs #1586) by @orieg in #1589
- fix(bench): distinct per-thread key streams in reclaim_stall, withhold the multi-writer figures by @orieg in #1591
- fix(bench): host guard excludes samples inside driver-recorded pauses (strmap_poll_stall, Refs #1586) by @orieg in #1592
- docs(bench): record the second discarded §44 run and its causes (Refs #1586) by @orieg in #1593
- eval(strmap): §44 outcome, S1 PASS and M1 MATERIAL (Refs #1586) by @orieg in #1594
- docs(strmap): state the writer stall of mem_used and mem_held, and how to budget without it (Refs #1586) by @orieg in #1595
- refactor(strmap): plain/shared dispatch moves to the call sites (Refs #1586) by @orieg in #1597
- eval(strmap): pre-register design B, METHODOLOGY §47 (Refs #1586) by @orieg in #1598
- chore(deps): ctutils 0.4.5 for RUSTSEC-2026-0385 by @orieg in #1600
- fix(bench): writer_scaling provenance labels a base-ref build outside the repository (Refs #1586) by @orieg in #1601
- feat(strmap): design B, shells and suffixes through NodeAlloc and a lock-free mem_used (Refs #1586) by @orieg in #1599
- fix(strmap): stats() walks sub-maps whose branch counts optimistic writers left stale (Refs #1603) by @orieg in #1605
- fix(strmap): count a dirty sub-map only to the root-leaf cap before an exclusive removal (Refs #1602) by @orieg in #1606
- eval(strmap): §48.4, G48-stall passes on the reference host (Refs #1602) by @orieg in #1607
- ci(miri): run the C ABI Miri step on Tier-1 shard 2 for headroom on shard 3 by @orieg in #1609
- docs: cite the re-measurement tracking issue beside each design-B pending marker (Refs #1611) by @orieg in #1614
- chore(release): 0.16.0 by @orieg in #1615
Full Changelog: v0.15.0...v0.16.0