Skip to main content

Tx

Struct Tx 

Source
pub struct Tx<'d> { /* private fields */ }

Implementations§

Source§

impl<'d> Tx<'d>

Source

pub fn push<N>( self, queue: &Arc<Queue<N>>, payload: N::Payload, not_before: Option<SystemTime>, ) -> DbResult<Self>
where N: QueueSpec, N::Mode: QueueTx<N>,

Enqueue a queue row as part of this transaction.

Source

pub fn ack<N>(self, queue: &Arc<Queue<N>>, token: LeaseToken) -> DbResult<Self>
where N: QueueSpec, N::Mode: QueueTx<N>,

Delete a queue row only if the supplied lease epoch remains current.

Source

pub fn nack<N>(self, queue: &Arc<Queue<N>>, token: LeaseToken) -> DbResult<Self>
where N: QueueSpec, N::Mode: QueueTx<N>,

Reschedule or dead-letter a queue row under the same T5 commit.

Source§

impl<'d> Tx<'d>

Source

pub fn put<S>( self, collection: &Arc<S>, key: &S::Key, value: &S::Val, ) -> DbResult<Self>
where S: TxTarget + TxParticipant + 'static,

Source

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.

Source

pub fn delete<S>(self, collection: &Arc<S>, key: &S::Key) -> DbResult<Self>
where S: TxTarget + TxParticipant + 'static,

Source

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.

Source

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.

Source

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.

Source

pub fn reserve<N, S>( self, ns: &Arc<UniqueIndex<N>>, key: N::SelfId, source: &Arc<S>, owner_key: &S::Key, ttl: Duration, ) -> DbResult<Self>
where N: UniqueNamespace, N::SelfId: Key + Send + Sync + Hash + Eq, S: TxTarget + TxParticipant + 'static,

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.

Source

pub fn release<N, S>( self, ns: &Arc<UniqueIndex<N>>, key: N::SelfId, source: &Arc<S>, owner_key: &S::Key, ) -> DbResult<Self>
where N: UniqueNamespace, N::SelfId: Key + Send + Sync + Hash + Eq, S: TxTarget + TxParticipant + 'static,

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.

Source

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.

Source

pub fn commit(self) -> DbResult<()>

Auto Trait Implementations§

§

impl<'d> !RefUnwindSafe for Tx<'d>

§

impl<'d> !Send for Tx<'d>

§

impl<'d> !Sync for Tx<'d>

§

impl<'d> !UnwindSafe for Tx<'d>

§

impl<'d> Freeze for Tx<'d>

§

impl<'d> Unpin for Tx<'d>

§

impl<'d> UnsafeUnpin for Tx<'d>

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

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<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more