Available now (0.5, feature-complete):
- 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
- Serializable (SSI) — opt-in read-set validation under the
serializablefeature, rejecting write skew and the read-only anomaly - Durable commit log — under the
durabilityfeature,Db::openlogs each commit to awal-dbwrite-ahead log and syncs before acknowledging; the log is replayed on restart - Garbage collection —
Db::collect_garbagereclaims versions no live transaction or snapshot can observe; an oldest-reader watermark guarantees a held snapshot's versions are never reclaimed - Write-write conflict detection — first-committer-wins at commit; the later writer is told to retry with a typed, retryable error
- Sharded commit path — lock-free timestamp allocation and per-shard conflict checks, so commits to unrelated keys do not contend (loom-checked)
- Pluggable backing store — the version store is the
VersionStoretrait; an in-memory store ships, and any backend (an LSM tree, a B-tree, a remote store) plugs in unchanged
Installation
[]
= "1.0"
# Opt into serializable isolation and/or a durable commit log:
= { = "1.0", = ["serializable", "durability"] }
Quick start
For a single read or write, skip the ceremony — get, put, and delete on
the database run in their own transaction (writes retry on conflict):
use Db;
let db = new;
db.put?;
assert_eq!;
# Ok::
When several operations must be atomic, open a transaction: begin, read and write through it, commit.
use Db;
let db = new;
// Write two keys in one atomic transaction.
let mut tx = db.begin;
tx.put;
tx.put;
tx.commit?;
// A later transaction reads a consistent snapshot.
let tx = db.begin;
assert_eq!;
# Ok::
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 Db;
let db = new;
let mut a = db.begin;
let mut b = db.begin;
a.put;
b.put;
a.commit?; // first committer wins
let err = b.commit.unwrap_err; // second is rejected
assert!; // retry against the fresh snapshot
# Ok::
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.
Serializable isolation
Snapshot isolation still allows write skew: two transactions that read the same
rows and write different ones can both commit, breaking an invariant that ties
those rows together. With the serializable feature,
begin_serializable validates a transaction's read set
at commit and rejects exactly those cases.
#
#
# Ok::
See examples/serializable_doctors.rs for the
full on-call-doctors demonstration, side by side under both isolation levels.
Durability
With the durability feature, Db::open backs the database with a wal-db
write-ahead log. Each commit's record is appended and synced before commit
returns, so an acknowledged commit survives a crash; on restart the log is
replayed and uncommitted work leaves no trace.
#
#
# Ok::
See examples/durable_store.rs for a commit /
drop / reopen walkthrough.
Garbage collection
Every write keeps the previous version so in-flight readers see a stable
snapshot, so versions accumulate. Db::collect_garbage reclaims the versions no
live transaction or snapshot can still observe and returns how many it removed.
A held snapshot pins the versions it can see, so collection never reclaims data
a live reader depends on.
use Db;
let db = new;
for v in 0..100u8
// With no snapshot held, only the newest version need be kept.
let reclaimed = db.collect_garbage;
assert!;
assert_eq!;
# Ok::
See examples/garbage_collection.rs for a
demonstration of a held snapshot pinning versions against collection.
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. |
serializable_doctors |
Write skew under SI vs serializable (needs --features serializable). |
durable_store |
Commit, drop, reopen — recovery from the log (needs --features durability). |
garbage_collection |
Reclaiming old versions; a held snapshot pins what it can see. |
Status
This is 1.0 — stable. The engine is feature-complete (snapshot and
serializable isolation, sharded lock-free commits, a durable wal-db commit log,
watermark garbage collection), tuned, hardened against adversarial schedules, and
benchmarked honestly. The public API is frozen until 2.0 and the durable
commit-log format is frozen for the 1.x series
(docs/COMMIT_LOG_FORMAT.md). See
docs/API.md for the full surface and
docs/PERFORMANCE.md for hot-path and comparison
numbers.
Where It Fits
txn-db is the transaction layer. It builds on:
wal-db— durable transaction commit loglsm-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.