# txn-db v0.4.0 — Durable commit log

**Committed transactions now survive a crash.** v0.4.0 adds a write-ahead commit
log via `wal-db`: open a database with `Db::open`, and every commit is appended
and synced before it is acknowledged, then replayed on the next start. Durability
is entirely opt-in — the in-memory `Db::new` path is unchanged.

## What is txn-db?

A multi-version concurrency control (MVCC) transaction engine: the layer that
turns a key-value store into a transactional database. Every write produces a new
version tagged with a commit timestamp, so readers get a stable snapshot without
ever blocking writers, and writers detect conflicts at commit time. The version
store is a trait, so the engine composes on top of any backend that can keep
timestamped versions.

## What's new in 0.4.0

### A durable commit log

With the `durability` feature, `Db::open(path)` backs the database with a
`wal-db` write-ahead log. Each committed transaction's record is appended and
synced before `commit` returns, so an acknowledged commit is on stable storage.
On open, the log is replayed and the committed transactions are reinstated.

```rust
# #[cfg(feature = "durability")]
# {
# let dir = tempfile::tempdir().unwrap();
# let path = dir.path().join("txn.wal");
use txn_db::Db;

// First run: commit, then the process exits.
{
    let db = Db::open(&path)?;
    let mut tx = db.begin();
    tx.put(b"k".to_vec(), b"v".to_vec());
    tx.commit()?;
}

// Restart: the log is replayed and the committed write is back.
let db = Db::open(&path)?;
assert_eq!(db.begin().get(b"k")?.as_deref(), Some(&b"v"[..]));
# }
# Ok::<(), txn_db::TxnError>(())
```

### Recovery with nothing to undo

Only *committed* transactions are ever logged — a transaction that aborts at
conflict detection, or that the process never managed to make durable, never
reaches the log. So recovery is simply a replay: there is no uncommitted state to
roll back. Records are sorted by commit timestamp before replay (commits append
after applying, possibly concurrently, so the log order is not necessarily the
timestamp order), and commit timestamps resume strictly after the highest
recovered one.

`wal-db` frames each record with its own length and CRC32C and truncates a torn
record at the tail when the log is opened, so a crash mid-append leaves a clean
prefix. txn-db's own commit-record decoder validates every length against the
bytes present, so a corrupt record can never drive an out-of-bounds read or an
unbounded allocation.

### `TxnError::Durability`

A new error variant surfaces a log that cannot be written, synced, or decoded. A
commit that fails to become durable is never acknowledged — so the contract that
an acknowledged commit survives a crash holds — and the variant is reported as
fatal, since a durability failure puts the crash guarantee in doubt.

## Testing

The v0.4 crash-recovery contract is verified with integration and property tests:

- Committed transactions survive a drop-and-reopen; rolled-back and dropped
  transactions do not.
- Tombstones (deletes) persist across reopen.
- Commit timestamps continue strictly after the highest recovered timestamp.
- A **property test** writes commits to a log, truncates it at an arbitrary byte
  offset, and asserts recovery yields a clean prefix of commits — never a
  half-applied transaction, never a panic.
- The commit-record decoder is fuzzed by property over truncations and rejects
  implausible lengths, bad tags, and trailing bytes.

## Breaking changes

**None.** `Db::open`, the `durability` feature, and `TxnError::Durability` are
all additive. Existing code, and the in-memory `Db::new` path, are unchanged.

## Verification

Run on Windows x86_64 (Rust stable 1.95.x and MSRV 1.85) and on Linux (WSL2
Ubuntu); the configured CI matrix runs the same on Linux, macOS, and Windows:

```bash
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
cargo test                       # default features
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
cargo +1.85 clippy --all-targets --all-features -- -D warnings
cargo +1.85 test --all-features
RUSTFLAGS="--cfg loom" cargo test --test loom_txn --release
cargo build --examples --all-features
cargo deny check
cargo audit
```

All green. Counts at this tag (`--all-features`): 37 unit + 16 integration
(6 durability + 4 serializable property + 4 snapshot-isolation property + 2
concurrency) + 31 doctests, plus 2 `loom` model checks.

## What's next

- **0.5.0 — Garbage collection + backing-store integration + feature freeze.**
  Reclaim versions no live snapshot can observe (watermark-driven), compose over
  `lsm-db` as a backing version store, and declare the feature set complete.

## Installation

```toml
[dependencies]
txn-db = "0.4"

# Opt into serializable isolation and/or a durable commit log:
txn-db = { version = "0.4", features = ["serializable", "durability"] }
```

MSRV: Rust 1.85 (the 2024 edition).

## Documentation

- [README](https://github.com/jamesgober/txn-db/blob/main/README.md)
- [API Reference](https://github.com/jamesgober/txn-db/blob/main/docs/API.md)
- [CHANGELOG](https://github.com/jamesgober/txn-db/blob/main/CHANGELOG.md)

---

**Full diff:** [`v0.3.0...v0.4.0`](https://github.com/jamesgober/txn-db/compare/v0.3.0...v0.4.0).
**Changelog:** [`CHANGELOG.md`](https://github.com/jamesgober/txn-db/blob/main/CHANGELOG.md#040---2026-06-07).
