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). 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>
impl<S: VersionStore> Transaction<S>
Sourcepub fn read_timestamp(&self) -> Timestamp
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);Sourcepub fn get(&self, key: &[u8]) -> Result<Option<Arc<[u8]>>>
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 deleteSourcepub fn put(&mut self, key: impl Into<Arc<[u8]>>, value: impl Into<Arc<[u8]>>)
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"[..]));Sourcepub fn delete(&mut self, key: impl Into<Arc<[u8]>>)
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);Sourcepub fn commit(self) -> Result<Timestamp>
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);Sourcepub fn rollback(self)
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);