Skip to content

Release v0.3.0

Choose a tag to compare

@anidotnet anidotnet released this 05 Jun 12:05
· 61 commits to main since this release

[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.x0.3.0 bump.

⚠️ Breaking Changes

  • nitriteNitriteError::cause() now returns Option<&NitriteError> instead of
    Option<&Box<NitriteError>>. Most call sites are unaffected thanks to deref coercion; code
    that named the &Box<…> type explicitly must drop the Box.
  • nitrite-fjall-adapter — the default storage durability is now Durability::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 on close()) 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 — a find() 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): a Durability { OnCommit, Periodic }
    enum with FjallModuleBuilder::durability(..) and FjallConfig::durability(); a bounded
    background fsync interval (default 1000 ms) so Periodic has 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 one fjall::WriteTransaction via 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), a contains_key normalization 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, and find(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/vec cursors still cache for cheap replay).
  • Fewer write-path clones: NitriteStore::with_atomic relaxed from Fn to FnOnce so
    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 fallible decode_value, surfacing damaged or format-incompatible bytes as a recoverable
    NitriteError instead of crashing the process; the write path likewise handles serialization
    errors without panicking.
  • contains_key numeric-key consistency: contains_key now applies the same numeric-type
    normalization as get/put/remove, so a key stored as one numeric type is found regardless
    of the numeric type queried.
  • NitriteError::cause() returns &NitriteError (removed the redundant Box).
  • Workspace-wide clippy/lint cleanup (correctness lints in tests, deprecated criterion::black_box,
    redundant clones, and more).