Skip to main content

Transaction

Struct Transaction 

Source
pub struct Transaction<S: VersionStore = MemoryStore> { /* private fields */ }
Expand description

A read-write transaction over a consistent snapshot of the database.

A transaction is created by Db::begin (snapshot isolation) or Db::begin_serializable (serializable, with the serializable feature). It reads as of the snapshot timestamp captured at that moment, so concurrent commits by other transactions are invisible to it. Writes are buffered in the transaction and become visible to others only when commit succeeds; within the transaction, a read of a key it has written returns that pending write (read-your-own-writes).

At commit the database checks every written key for a write-write conflict: if another transaction committed a change to any of those keys after this transaction’s snapshot, the commit is rejected with a retryable TxnError::Conflict and none of the writes are applied. A serializable transaction also validates its read set, rejecting commits whose reads are no longer current.

Dropping a transaction without committing discards its buffered writes; it is equivalent to rollback.

§Examples

use txn_db::Db;

let db = Db::new();

let mut tx = db.begin();
tx.put(b"account:1".to_vec(), 100u64.to_le_bytes().to_vec());
tx.put(b"account:2".to_vec(), 50u64.to_le_bytes().to_vec());
let commit_ts = tx.commit()?;

// A fresh transaction sees the committed state.
let tx = db.begin();
assert!(tx.get(b"account:1")?.is_some());
assert!(commit_ts > txn_db::Timestamp::ZERO);

Implementations§

Source§

impl<S: VersionStore> Transaction<S>

Source

pub fn read_timestamp(&self) -> Timestamp

The snapshot timestamp this transaction reads at.

Every read that is not served from the transaction’s own write buffer observes the database as of this timestamp.

§Examples
use txn_db::Db;

let db = Db::new();
let tx = db.begin();
// Nothing has committed yet, so the snapshot is the empty database.
assert_eq!(tx.read_timestamp(), txn_db::Timestamp::ZERO);
Source

pub fn get(&self, key: &[u8]) -> Result<Option<Arc<[u8]>>>

Read the value of key as this transaction sees it.

If the transaction has written key, the pending write is returned (read-your-own-writes), including None if it has deleted the key. Otherwise the value is read from the transaction’s snapshot: the newest version committed at or before the snapshot timestamp, or None if the key does not exist as of the snapshot. For a serializable transaction the key is recorded in the read set for validation at commit.

§Errors

Returns TxnError::Store if the backing VersionStore fails the read. The default in-memory store never fails.

§Examples
use txn_db::Db;

let db = Db::new();
let mut tx = db.begin();

assert_eq!(tx.get(b"k")?, None);          // absent
tx.put(b"k".to_vec(), b"v".to_vec());
assert_eq!(tx.get(b"k")?.as_deref(), Some(&b"v"[..])); // its own write
tx.delete(b"k".to_vec());
assert_eq!(tx.get(b"k")?, None);          // its own delete
Source

pub fn put(&mut self, key: impl Into<Arc<[u8]>>, value: impl Into<Arc<[u8]>>)

Buffer a write of value to key, to be applied at commit.

The write is local to this transaction until commit succeeds; other transactions do not see it. Writing the same key twice keeps the last value. Both arguments accept anything convertible into an Arc<[u8]> — passing an owned Vec<u8> or Arc<[u8]> moves it in without copying the bytes.

§Examples
use txn_db::Db;

let db = Db::new();
let mut tx = db.begin();
tx.put(b"city".to_vec(), b"oslo".to_vec());
tx.put(b"city".to_vec(), b"bergen".to_vec()); // overwrites within the txn
assert_eq!(tx.get(b"city")?.as_deref(), Some(&b"bergen"[..]));
Source

pub fn delete(&mut self, key: impl Into<Arc<[u8]>>)

Buffer a delete of key, to be applied at commit.

After this call the transaction reads key as absent. At commit a tombstone is written so that snapshots taken after the commit also see the key as absent. Deleting a key that does not exist is a no-op that still participates in conflict detection, so a delete races other writers the same way a put does.

§Examples
use txn_db::Db;

let db = Db::new();
let mut setup = db.begin();
setup.put(b"k".to_vec(), b"v".to_vec());
setup.commit()?;

let mut tx = db.begin();
tx.delete(b"k".to_vec());
tx.commit()?;

assert_eq!(db.begin().get(b"k")?, None);
Source

pub fn commit(self) -> Result<Timestamp>

Commit the transaction, applying all buffered writes atomically.

On success every buffered write becomes visible to transactions that begin afterward, and the commit timestamp is returned. A transaction that buffered no writes commits trivially and returns its snapshot timestamp without allocating a new one — including a serializable read-only transaction, which has observed a consistent snapshot and needs no validation.

§Errors

Returns TxnError::Conflict — which is retryable — if any written key was changed by another transaction that committed after this one’s snapshot, or, for a serializable transaction, if any key it read has since changed. In either case no writes are applied. Returns TxnError::Store if the backing store fails to apply the batch.

§Examples
use txn_db::Db;

let db = Db::new();
let mut tx = db.begin();
tx.put(b"k".to_vec(), b"v".to_vec());
let ts = tx.commit()?;
assert!(ts > txn_db::Timestamp::ZERO);
Source

pub fn rollback(self)

Discard the transaction and all of its buffered writes.

This is explicit; simply dropping the transaction has the same effect. Rolling back never fails and never touches the shared store.

§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.rollback();

// The write never reached the database.
assert_eq!(db.begin().get(b"k")?, None);

Auto Trait Implementations§

§

impl<S = MemoryStore> !Freeze for Transaction<S>

§

impl<S = MemoryStore> !RefUnwindSafe for Transaction<S>

§

impl<S = MemoryStore> !Sync for Transaction<S>

§

impl<S> Send for Transaction<S>

§

impl<S> Unpin for Transaction<S>

§

impl<S> UnsafeUnpin for Transaction<S>

§

impl<S> UnwindSafe for Transaction<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> 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, 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