txn-db 0.2.0

MVCC transaction engine for Rust storage layers. Snapshot isolation and serializable transactions with multi-version concurrency control, conflict detection, and a durable transaction log on wal-db. The transaction layer for embedded databases and Hive DB.
Documentation

Available now (0.2):

  • MVCC — each write creates a new version; readers see a consistent snapshot without blocking writers
  • Snapshot isolation — a transaction reads the database as of its start timestamp; its own writes are visible to itself before commit
  • Write-write conflict detection — first-committer-wins at commit; the later writer is told to retry with a typed, retryable error
  • Pluggable backing store — the version store is the VersionStore trait; an in-memory store ships, and any backend plugs in

On the roadmap:

  • Serializable (SSI) — optional serializable isolation via read/write conflict tracking
  • Durable txn log — commits logged to wal-db before acknowledgment (under durability)
  • Garbage collection — old versions reclaimed once no live snapshot can observe them

Installation

[dependencies]
txn-db = "0.2"

Quick start

The whole common case is begin, read and write through the transaction, commit:

use txn_db::Db;

let db = Db::new();

// Write two keys in one atomic transaction.
let mut tx = db.begin();
tx.put(b"user:1:name".to_vec(), b"ada".to_vec());
tx.put(b"user:1:role".to_vec(), b"admin".to_vec());
tx.commit()?;

// A later transaction reads a consistent snapshot.
let tx = db.begin();
assert_eq!(tx.get(b"user:1:name")?.as_deref(), Some(&b"ada"[..]));
# Ok::<(), txn_db::TxnError>(())

When two transactions race to write the same key, the first to commit wins and the second is told to retry — that is what prevents lost updates:

use txn_db::Db;

let db = Db::new();
let mut a = db.begin();
let mut b = db.begin();
a.put(b"counter".to_vec(), b"1".to_vec());
b.put(b"counter".to_vec(), b"2".to_vec());

a.commit()?;                          // first committer wins
let err = b.commit().unwrap_err();    // second is rejected
assert!(err.is_retryable());          // retry against the fresh snapshot
# Ok::<(), txn_db::TxnError>(())

The retry loop is a few lines; see examples/concurrent_counter.rs for the contended read-modify-write pattern, examples/bank_transfer.rs for an atomic multi-key transfer, and examples/custom_store.rs for plugging in your own VersionStore.

Examples

Example What it shows
quick_start Shortest end-to-end: open, write, read back.
bank_transfer Atomic multi-key update with conflict retries.
concurrent_counter Many threads increment one key; no update is lost.
snapshot_reads A snapshot stays stable as the database moves on.
custom_store Backing the engine with a custom VersionStore.
cargo run --example quick_start

Status

This is the 0.2 foundation: the public surface, the MVCC core, snapshot isolation, and write-write conflict detection over an in-memory store. The docs/API.md reference documents the full Tier-1 surface, and the remaining phases — serializable isolation, durable commits, and garbage collection — follow per the roadmap. The shape of the Tier-1 API is settled and will not change before 1.0.

Where It Fits

txn-db is the transaction layer. It builds on:

  • wal-db — durable transaction commit log
  • lsm-db — a natural backing version store
  • Hive DB — the transaction orchestration layer (DISTRO) builds on these semantics

It stays foreign-compatible: usable standalone over any version store that implements the trait.

Contributing

Before opening a PR, cargo fmt --all, cargo clippy --all-targets --all-features -- -D warnings, and cargo test --all-features must be clean. Hot-path changes require a criterion benchmark; correctness-critical paths require property and/or loom tests.