pub struct Db<S: VersionStore = MemoryStore> { /* private fields */ }Expand description
A transactional, multi-version key-value database.
Db is the front door. Db::new gives you an in-memory database;
Db::with_store builds one over any VersionStore. From there the whole
common case is begin / get /
put / commit,
with snapshot for read-only point-in-time views.
Transactions default to snapshot isolation. With the serializable
feature enabled, begin_serializable starts a
transaction whose read set is validated at commit, rejecting write skew and
the other anomalies snapshot isolation permits.
A Db is a clonable handle over shared state, like an Arc. Cloning it
is cheap and every clone refers to the same database, so the idiomatic way
to use it across threads is to clone a handle per thread.
§Examples
The four-call common case:
use txn_db::Db;
let db = Db::new();
let mut tx = db.begin();
tx.put(b"greeting".to_vec(), b"hei".to_vec());
tx.commit()?;
let tx = db.begin();
assert_eq!(tx.get(b"greeting")?.as_deref(), Some(&b"hei"[..]));Sharing one database across threads:
use std::thread;
use txn_db::Db;
let db = Db::new();
let handles: Vec<_> = (0..4u8)
.map(|i| {
let db = db.clone();
thread::spawn(move || {
let mut tx = db.begin();
tx.put(vec![i], vec![i]);
// Independent keys never conflict.
tx.commit().expect("commit");
})
})
.collect();
for h in handles {
h.join().expect("thread");
}Implementations§
Source§impl Db<MemoryStore>
impl Db<MemoryStore>
Sourcepub fn new() -> Self
pub fn new() -> Self
Create an empty in-memory database.
This is the default configuration: a MemoryStore backing store, ready
for begin.
§Examples
use txn_db::Db;
let db = Db::new();
assert_eq!(db.last_committed(), txn_db::Timestamp::ZERO);Source§impl<S: VersionStore> Db<S>
impl<S: VersionStore> Db<S>
Sourcepub fn with_store(store: S) -> Self
pub fn with_store(store: S) -> Self
Create a database over a custom VersionStore.
This is the Tier-3 seam: supply any backing store and the transaction semantics — snapshot isolation, read-your-own-writes, conflict detection — compose on top of it unchanged.
§Examples
use txn_db::{Db, MemoryStore};
let db = Db::with_store(MemoryStore::new());
let mut tx = db.begin();
tx.put(b"k".to_vec(), b"v".to_vec());
tx.commit()?;Sourcepub fn begin(&self) -> Transaction<S>
pub fn begin(&self) -> Transaction<S>
Begin a snapshot-isolation transaction over the current state.
The transaction takes its snapshot at this moment: it reads as of the
most recent commit and is unaffected by commits that happen afterward.
Its writes are checked for write-write conflicts at commit, but its reads
are not validated — use begin_serializable
when you need serializability.
§Examples
use txn_db::Db;
let db = Db::new();
let mut tx = db.begin();
tx.put(b"k".to_vec(), b"v".to_vec());
tx.commit()?;Sourcepub fn begin_serializable(&self) -> Transaction<S>
Available on crate feature serializable only.
pub fn begin_serializable(&self) -> Transaction<S>
serializable only.Begin a serializable transaction over the current state.
A serializable transaction tracks every key it reads and, at commit, validates that none of them changed since its snapshot — in addition to the write-write check every transaction gets. That read-set validation is what rejects write skew and the read-only anomaly that plain snapshot isolation permits, giving serializable behavior for the transactions that commit writes. A serializable transaction that writes nothing commits trivially, exactly like a read-only snapshot.
Available with the serializable feature. Snapshot isolation remains the
default and is unaffected.
§Examples
use txn_db::Db;
let db = Db::new();
// Seed two rows that an invariant ties together.
let mut tx = db.begin();
tx.put(b"on_call:alice".to_vec(), vec![1]);
tx.put(b"on_call:bob".to_vec(), vec![1]);
tx.commit()?;
// A serializable transaction validates the rows it read at commit.
let mut tx = db.begin_serializable();
let _alice = tx.get(b"on_call:alice")?;
let _bob = tx.get(b"on_call:bob")?;
tx.put(b"on_call:alice".to_vec(), vec![0]);
tx.commit()?;Sourcepub fn snapshot(&self) -> Snapshot<S>
pub fn snapshot(&self) -> Snapshot<S>
Take a read-only snapshot of the current state of the database.
The returned Snapshot reads as of this instant and never changes,
even as other transactions commit. Use it to read several keys at one
consistent point in time without the overhead of a transaction.
§Examples
use txn_db::Db;
let db = Db::new();
let snap = db.snapshot();
assert_eq!(snap.get(b"k")?, None);Sourcepub fn last_committed(&self) -> Timestamp
pub fn last_committed(&self) -> Timestamp
The timestamp of the most recent commit visible to a new transaction.
Returns Timestamp::ZERO for a database that has never been written.
This is the read watermark: the timestamp a transaction beginning now
would read at.
§Examples
use txn_db::Db;
let db = Db::new();
assert_eq!(db.last_committed(), txn_db::Timestamp::ZERO);
let mut tx = db.begin();
tx.put(b"k".to_vec(), b"v".to_vec());
let ts = tx.commit()?;
assert_eq!(db.last_committed(), ts);