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. It reads as of the snapshot timestamp captured at that moment, so concurrent commits by other transactions are invisible to it — this is snapshot isolation. 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. This is what prevents lost updates.

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.

§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.

§Errors

Returns TxnError::Conflict — which is retryable — if any written key was changed by another transaction that committed after this one’s snapshot; in that 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> Freeze for Transaction<S>

§

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

§

impl<S> Send for Transaction<S>

§

impl<S> Sync 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