Release v0.3.0
[0.3.0] - 2026-06-05
This release makes cross-partition writes crash-atomic, adds configurable durability, and
substantially speeds up indexed range queries, counts, full-text indexing, and large-result
iteration. It contains breaking changes (see below), hence the 0.2.x → 0.3.0 bump.
⚠️ Breaking Changes
nitrite—NitriteError::cause()now returnsOption<&NitriteError>instead of
Option<&Box<NitriteError>>. Most call sites are unaffected thanks to deref coercion; code
that named the&Box<…>type explicitly must drop theBox.nitrite-fjall-adapter— the default storage durability is nowDurability::Periodic
(commits are buffered to the OS and fsynced by a background timer within ~1s) rather than an
fsync on every commit. A process crash never loses an acknowledged write; a power loss can
lose up to ~1s of acknowledged writes by default. Opt back into per-commit fsync with
FjallModule::with_config().durability(Durability::OnCommit).nitrite-tantivy-fts— full-text writes/deletes are now batched and committed on the next
search (or onclose()) instead of once per document. Searches still observe their own writes;
an unclean crash loses the uncommitted FTS batch (the index is derived and rebuildable).nitrite— afind()cursor is now streaming:reset()re-runs the query instead of
replaying a cached snapshot. Observable results are unchanged for a stable collection; a
cursor reset after the underlying data has changed now reflects the current data.
Added
- Configurable durability (
nitrite-fjall-adapter): aDurability { OnCommit, Periodic }
enum withFjallModuleBuilder::durability(..)andFjallConfig::durability(); a bounded
background fsync interval (default 1000 ms) soPeriodichas a bounded power-loss window. - Atomic cross-partition transactions (
nitrite,nitrite-fjall-adapter): a logical write
(a transaction commit, or a single insert/update/remove and all of its index updates) now
lands in onefjall::WriteTransactionvia a scoped thread-local bridge, so data and index
partitions commit — and recover — together. Durability-aware regression suite: crash/reopen consistency tests, exact-result range
tests (single-field, compound-terminal,between), acontains_keynormalization test, and a
streaming-cursor reset/replay test; plus criterion benchmarks for range, count, and FTS.
Changed / Performance
- Crash-atomic close/reopen:
close()drains (persist + bounded wait on compactions) so a
subsequent open observes a fully consistent state (no index entry without its data row). - Indexed range queries are now actually index-accelerated. Multi-bound ranges
(x >= a AND x <= b,between, and the range on a compound index's terminal field) drive a
single bounded index scan (ceiling(a)..floor(b)) instead of a one-sided scan plus a
post-fetch filter — narrow ranges no longer fetch nearly the whole collection (~40% faster vs
full scan at 10k rows, growing with size). count()/size()short-circuit: index-covered queries answer from the index id-set
length, andfind(all())from the map size — without fetching any document (~26× faster at
~800 matches, ~121× at ~8000 matches).- Full-text indexing (
nitrite-tantivy-fts): batched commits + a single reused reader make
bulk indexing ~87× faster (100-document insert: 14.85 s → 0.17 s). - Streaming cursors: forward-only iteration retains O(1) documents instead of the entire
result set (joins and raw/veccursors still cache for cheap replay). - Fewer write-path clones:
NitriteStore::with_atomicrelaxed fromFntoFnOnceso
inserts/updates/removes move their input into the atomic scope instead of cloning it (single
insert ~10% faster; no transient full-batch document clone).
Fixed
- Corrupted/foreign on-disk data no longer panics. The Fjall read path deserializes through
a fallibledecode_value, surfacing damaged or format-incompatible bytes as a recoverable
NitriteErrorinstead of crashing the process; the write path likewise handles serialization
errors without panicking. contains_keynumeric-key consistency:contains_keynow applies the same numeric-type
normalization asget/put/remove, so a key stored as one numeric type is found regardless
of the numeric type queried.NitriteError::cause()returns&NitriteError(removed the redundantBox).- Workspace-wide clippy/lint cleanup (correctness lints in tests, deprecated
criterion::black_box,
redundant clones, and more).