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