Skip to main content

Db

Struct Db 

Source
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>

Source

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

pub fn open(path: impl AsRef<Path>) -> Result<Db<MemoryStore>>

Available on crate feature durability only.

Open a durable database backed by a write-ahead log at path, replaying any committed transactions already in the log.

Every transaction committed against the returned database appends its record to the log and syncs it before commit returns, so an acknowledged commit survives a crash. On open, the log is replayed: each committed transaction is reinstated, and a transaction that never reached the log — because it aborted, or because the process crashed before its record was made durable — is simply absent. The recovered data lives in memory; the log is the durable record from which it is rebuilt.

Available with the durability feature.

§Errors

Returns TxnError::Durability if the log cannot be opened or a record read back from it does not decode.

§Examples
use txn_db::Db;

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

// Reopening replays the log: the committed write is still there.
let db = Db::open(&path)?;
assert_eq!(db.begin().get(b"k")?.as_deref(), Some(&b"v"[..]));
Source§

impl<S: VersionStore> Db<S>

Source

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()?;
Source

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 (with the serializable feature) 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()?;
Source

pub fn begin_serializable(&self) -> Transaction<S>

Available on crate feature 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()?;
Source

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);
Source

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);
Source

pub fn collect_garbage(&self) -> usize

Reclaim versions that no live transaction or snapshot can observe, returning how many were removed.

txn-db keeps every version of a key so that an in-flight reader sees a stable snapshot. Once no live reader can observe an old version — because every active transaction and snapshot reads at a timestamp newer than a later version of that key — the old one is unreachable and this reclaims it. A key deleted before the oldest live reader’s snapshot is dropped entirely.

Call it periodically, or after retiring long-running snapshots, to bound memory. It is safe to call at any time and from any thread: a version a live reader can still reach is never reclaimed. With the default in-memory store this prunes the version chains; a custom VersionStore that keeps no history can leave the default no-op in place.

§Examples
use txn_db::Db;

let db = Db::new();
// Overwrite the same key several times.
for v in 0..5u8 {
    let mut tx = db.begin();
    tx.put(b"k".to_vec(), vec![v]);
    tx.commit()?;
}

// No snapshot is held, so only the newest version need be kept.
let reclaimed = db.collect_garbage();
assert!(reclaimed > 0);
assert_eq!(db.begin().get(b"k")?.as_deref(), Some(&[4u8][..]));

Trait Implementations§

Source§

impl<S: VersionStore> Clone for Db<S>

Source§

fn clone(&self) -> Self

Clone the handle, not the data: the clone shares the same underlying database.

1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Default for Db<MemoryStore>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<S> Freeze for Db<S>

§

impl<S> RefUnwindSafe for Db<S>
where S: RefUnwindSafe,

§

impl<S> Send for Db<S>

§

impl<S> Sync for Db<S>

§

impl<S> Unpin for Db<S>

§

impl<S> UnsafeUnpin for Db<S>

§

impl<S> UnwindSafe for Db<S>
where S: RefUnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<E> WithErrorCode<E> for E

Source§

fn with_code(self, code: impl Into<String>) -> CodedError<E>

Attach an error code to an error