#[non_exhaustive]pub enum TxnError {
Conflict {
key_len: usize,
},
Store {
context: &'static str,
detail: String,
},
Durability {
detail: String,
},
}Expand description
Everything that can go wrong while running a transaction.
The type is #[non_exhaustive]:
later versions may add variants without a major bump, so a match over it
must include a wildcard arm. Each variant documents what the caller should
do when they encounter it.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Conflict
A write-write conflict aborted the transaction at commit time.
Under snapshot isolation the database applies first-committer-wins: when a transaction commits, every key it wrote is checked against the version store, and if any of those keys was written by a different transaction that committed after this one took its snapshot, this commit is rejected. None of its writes are applied.
This is the mechanism that prevents lost updates, and it is a normal
part of operating under optimistic concurrency control. The correct
response is to retry: begin a fresh transaction, re-read, re-apply the
logic, and commit again. TxnError::is_retryable returns true for
this variant.
Only the length of the conflicting key is carried, never its bytes, so the error is safe to log even when keys hold sensitive data.
Store
The backing version store failed to service a read or apply a write.
The in-memory store that ships with txn-db never produces this; it is
the channel through which a custom VersionStore
— for example one backed by an on-disk engine — surfaces a failure
through the same Result. context names the operation that was
attempted (such as "read visible version"); detail carries the
store’s own message. Whether to retry depends on the store, so this
variant is reported as non-fatal and left for the caller to judge.
Fields
Durability
The durable commit log failed, or a record read back from it was not intact.
Produced only with the durability feature: when appending or syncing a
commit record fails, or when recovery on Db::open reads a
record whose bytes do not decode. A commit that fails to become durable
is not acknowledged — the contract that an acknowledged commit survives
a crash holds — but the failure is fatal in the sense that the database’s
durability guarantee is in question, so treat it as unrecoverable rather
than retrying blindly.
Implementations§
Source§impl TxnError
impl TxnError
Sourcepub fn conflict(key_len: usize) -> Self
pub fn conflict(key_len: usize) -> Self
Build a TxnError::Conflict for a key of the given length.
A custom VersionStore returns this from
try_commit when validation detects
that a written or read key changed after the transaction’s snapshot. Pass
the conflicting key’s length; its bytes are deliberately not carried, so
the error stays safe to log. The shipped in-memory store uses this
internally.
§Examples
use txn_db::TxnError;
let err = TxnError::conflict(b"account:42".len());
assert!(err.is_retryable());Sourcepub fn store(context: &'static str, detail: impl Display) -> Self
pub fn store(context: &'static str, detail: impl Display) -> Self
Build a TxnError::Store from a static context and a store message.
Intended for VersionStore implementations that
can fail; the in-memory store never calls it.
Sourcepub fn is_retryable(&self) -> bool
pub fn is_retryable(&self) -> bool
Returns true if re-running the transaction is the right response.
A Conflict is retryable: another transaction won
the race, and a fresh attempt against the newer snapshot will typically
succeed. Backing-store failures are reported as not retryable here
because their recoverability is store-specific; inspect the variant when
a store can distinguish transient from permanent faults.
§Examples
use txn_db::{Db, TxnError};
let db = Db::new();
// The common retry loop: keep trying while the commit is retryable.
let outcome = loop {
let mut tx = db.begin();
let current = tx.get(b"counter")?.map_or(0u64, |v| {
let mut buf = [0u8; 8];
buf.copy_from_slice(&v);
u64::from_le_bytes(buf)
});
tx.put(b"counter".to_vec(), (current + 1).to_le_bytes().to_vec());
match tx.commit() {
Ok(ts) => break ts,
Err(e) if e.is_retryable() => continue,
Err(e) => return Err(e),
}
};Trait Implementations§
impl Eq for TxnError
Source§impl Error for TxnError
impl Error for TxnError
1.30.0 · Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()
Source§impl ForgeError for TxnError
impl ForgeError for TxnError
Source§fn is_fatal(&self) -> bool
fn is_fatal(&self) -> bool
A Conflict is the retry signal and a
Store failure is the store’s to classify, so neither
is fatal. A Durability failure puts the crash
guarantee in doubt and is reported as fatal.