pub struct Tx<'d> { /* private fields */ }Implementations§
Source§impl<'d> Tx<'d>
impl<'d> Tx<'d>
Sourcepub fn push<N>(
self,
queue: &Arc<Queue<N>>,
payload: N::Payload,
not_before: Option<SystemTime>,
) -> DbResult<Self>
pub fn push<N>( self, queue: &Arc<Queue<N>>, payload: N::Payload, not_before: Option<SystemTime>, ) -> DbResult<Self>
Enqueue a queue row as part of this transaction.
Source§impl<'d> Tx<'d>
impl<'d> Tx<'d>
pub fn put<S>(
self,
collection: &Arc<S>,
key: &S::Key,
value: &S::Val,
) -> DbResult<Self>where
S: TxTarget + TxParticipant + 'static,
Sourcepub fn insert<S>(
self,
collection: &Arc<S>,
key: &S::Key,
value: &S::Val,
) -> DbResult<Self>where
S: TxTarget + TxParticipant + 'static,
pub fn insert<S>(
self,
collection: &Arc<S>,
key: &S::Key,
value: &S::Val,
) -> DbResult<Self>where
S: TxTarget + TxParticipant + 'static,
Write value at key only if the key is absent, failing the whole
transaction with DbError::KeyExists if it is not.
The transactional twin of a collection’s insert. Presence is decided
at commit() under the transaction’s shard locks, so two transactions
racing for the same free key produce exactly one winner.
§Failure is pre-log
The check runs in the commit’s evaluation round, before any GSN is
reserved and long before the log append: on KeyExists nothing is
written, no index row is projected, no counter moves, no hook fires and
no shard is fenced. The caller may retry or surface a client error
(KeyExists maps to HTTP 409).
§Relationship to guard + put
For a decodable stored value this is the same outcome as
tx.guard(&c, &k, |cur| if cur.is_none() { Ok(()) } else { Err(DbError::KeyExists) })?
.put(&c, &k, &v)?at one operation against max_ops instead of two, and with the key
written once instead of twice.
The two forms are not equivalent on an undecodable row.
guard hands its closure a typed value and therefore
decodes first, so a corrupt row makes it fail with the codec’s error;
insert tests only whether bytes are present and returns KeyExists.
That is deliberate — it is what a collection-level insert does, since
it rejects on the index before any decode. A uniqueness claim stays
rejected whether or not what already occupies the key can still be read.
guard remains the tool for the more general “absent or matching
what we expect” precondition; insert does not serve that case.
Within one transaction the check sees the transaction’s own pending
writes first: put then insert on one key is KeyExists, while
delete then insert succeeds.
pub fn delete<S>(self, collection: &Arc<S>, key: &S::Key) -> DbResult<Self>where
S: TxTarget + TxParticipant + 'static,
Sourcepub fn delete_scope<S, K, const N: usize>(
self,
scope: &TxScope<S, K, N>,
limit: usize,
) -> DbResult<(Self, Selection)>where
K: Key,
S: TxTarget<Key = K> + TxParticipant + 'static,
pub fn delete_scope<S, K, const N: usize>(
self,
scope: &TxScope<S, K, N>,
limit: usize,
) -> DbResult<(Self, Selection)>where
K: Key,
S: TxTarget<Key = K> + TxParticipant + 'static,
Queue bounded deletion of keys selected by scope.
This only builds source delete operations; it never writes before
commit. Selection::done is false whenever the caller’s
limit or remaining source-operation capacity truncated the range. It is
not a snapshot: a key inserted after selection can survive even when
done is true. A zero limit always returns
Selection { queued: 0, done: false }.
The selected source operations may still expand at commit time. Final
derived max_ops, record bytes, queue admission, and projection budgets
can reject the transaction before log append; rebuilding the same scope
with a smaller limit (normally halving it) can make those size-related
refusals fit. UniqueViolation, CounterTargetMissing,
AmbiguousCounterTarget, and decode failures are data validation errors:
they are also pre-log, but retrying a smaller chunk does not repair them.
A useful initial size is
chunk * (1 + derived rows per source row) <= max_ops.
Every call selects currently stored keys independently. Do not call this method repeatedly for the same scope on one transaction when the intent is to advance a cursor: queued deletes are not removed from its next storage walk. Commit one chunk, then start a new transaction for the next chunk.
Sourcepub fn upsert<S, U>(
self,
collection: &Arc<S>,
key: &S::Key,
f: impl FnOnce(Option<&S::Val>) -> U + 'static,
) -> DbResult<Self>where
S: TxTarget + TxParticipant + 'static,
U: IntoUpsert<S::Val> + 'static,
pub fn upsert<S, U>(
self,
collection: &Arc<S>,
key: &S::Key,
f: impl FnOnce(Option<&S::Val>) -> U + 'static,
) -> DbResult<Self>where
S: TxTarget + TxParticipant + 'static,
U: IntoUpsert<S::Val> + 'static,
Decide the write from the stored value, under the transaction’s locks.
§The closure runs under shard locks
f is evaluated at commit() while this thread holds every shard the
transaction touches. It must not reach back into the database: atomic()
and a nested commit() return DbError::ReentrantTx rather than
deadlock, but a facade call (put, get, delete, upsert on a
collection) is not instrumented and will park forever on a lock this
thread already owns. Read what you need before building the transaction.
Sourcepub fn guard<S>(
self,
collection: &Arc<S>,
key: &S::Key,
f: impl FnOnce(Option<&S::Val>) -> DbResult<()> + 'static,
) -> DbResult<Self>where
S: TxTarget + TxParticipant + 'static,
pub fn guard<S>(
self,
collection: &Arc<S>,
key: &S::Key,
f: impl FnOnce(Option<&S::Val>) -> DbResult<()> + 'static,
) -> DbResult<Self>where
S: TxTarget + TxParticipant + 'static,
Fail the whole transaction unless the stored value satisfies f.
Like upsert, f runs under the transaction’s shard
locks and must not reach back into the database — see that method’s
reentrancy note.
Sourcepub fn reserve<N, S>(
self,
ns: &Arc<UniqueIndex<N>>,
key: N::SelfId,
source: &Arc<S>,
owner_key: &S::Key,
ttl: Duration,
) -> DbResult<Self>
pub fn reserve<N, S>( self, ns: &Arc<UniqueIndex<N>>, key: N::SelfId, source: &Arc<S>, owner_key: &S::Key, ttl: Duration, ) -> DbResult<Self>
Claim key in ns on behalf of source/owner_key, before that
source row exists — a reservation held until ttl elapses, unless
the source row itself claims the same key first (in this transaction
or a later one), which promotes it to a permanent claim. That
promotion is not a special case: it is the ordinary “owner is me, has
a deadline” branch of unique::resolve::decide,
the same rule an explicit reserve and a projected
claim on the same key resolve through in one transaction — see
txn::projection_commit::resolve_claims’s doc for exactly how that
ordering is made deterministic.
Like put/delete, this only queues the
intent — nothing is written until commit. Unlike
them, the queued intent is not a raw write: it is decided in the
same claim-resolution stage a projected claim is
(txn::projection_commit::resolve_claims), so it obeys the same
release-before-acquisition partition instead of colliding with a
projected claim on the same key through an ordinary last-write-wins
overlay ([Action::Put]’s set_write merge).
ttl is validated here, before any lock is taken:
DbError::UniqueReservationInvalid with reason "zero duration"
for Duration::ZERO (an expired-on-arrival reservation is
indistinguishable from a leak — call release
instead), or "deadline overflow" for a duration whose millisecond
count does not fit u64. A sub-millisecond, non-zero duration rounds
up to 1 ms. The reservation’s actual deadline is not now() + ttl computed here — the resolver stamps it at commit(), under the
namespace’s own lock, from one clock read shared by every reservation
that commit resolves.
Sourcepub fn release<N, S>(
self,
ns: &Arc<UniqueIndex<N>>,
key: N::SelfId,
source: &Arc<S>,
owner_key: &S::Key,
) -> DbResult<Self>
pub fn release<N, S>( self, ns: &Arc<UniqueIndex<N>>, key: N::SelfId, source: &Arc<S>, owner_key: &S::Key, ) -> DbResult<Self>
Give up key in ns if source/owner_key currently holds it —
permanent claim or reservation alike. Idempotent: releasing an
already-free key is a no-op success at commit(). Refuses the whole
commit with DbError::UniqueViolation if a different owner holds
the key — a release never deletes a row it does not own, the same
rule a projected release follows
(unique::resolve::decide’s
foreign-owner arm, which every [crate::txn::participant::ClaimKind]
shares).
Like reserve, this queues a claim-stage intent, not
a raw write — see that method’s doc.
Sourcepub fn durable(self) -> Self
pub fn durable(self) -> Self
Wait for the log record to reach disk before the transaction is applied. Without it the transaction is atomic against a process crash but not against power loss.