Skip to main content

TrialLifecycleStorage

Trait TrialLifecycleStorage 

pub trait TrialLifecycleStorage: Send + Sync {
Show 27 methods // Required methods fn reserve_and_create_if_budget( &self, request: ReserveAndCreateRequest, ) -> Result<ReservationOutcome>; fn abandon(&self, request: AbandonRequest) -> Result<bool>; fn observe(&self, trial: TrialId) -> Result<LeaseObservation>; fn claim(&self, request: ClaimRequest) -> Result<TrialOwnerToken>; fn renew(&self, request: RenewRequest) -> Result<LeaseObservation>; fn fail_stale(&self, request: FailStaleRequest) -> Result<bool>; fn fenced_mutate(&self, request: FencedMutationRequest) -> Result<()>; fn terminal(&self, request: TerminalRequest) -> Result<()>; // Provided methods fn observe_finalization( &self, _study: StudyId, ) -> Result<FinalizationObservation> { ... } fn acquire_finalization( &self, _request: FinalizationAcquireRequest, ) -> Result<FinalizationToken> { ... } fn renew_finalization( &self, _token: FinalizationToken, ) -> Result<FinalizationObservation> { ... } fn release_finalization(&self, _token: FinalizationToken) -> Result<()> { ... } fn terminal_with_intent( &self, _request: TerminalRequest, _intent: FinalizationIntent, ) -> Result<OutboxKey> { ... } fn terminal_with_intent_owned( &self, _owner: FinalizationToken, _request: TerminalRequest, _intent: FinalizationIntent, ) -> Result<OutboxKey> { ... } fn advance_outbox( &self, _request: OutboxAdvanceRequest, ) -> Result<OutboxPhase> { ... } fn advance_outbox_with_operation( &self, request: OutboxAdvanceRequest, _operation: OperationId, ) -> Result<OutboxPhase> { ... } fn mutate_owned( &self, _owner: FinalizationToken, _request: FencedMutationRequest, ) -> Result<()> { ... } fn advance_outbox_owned( &self, _owner: FinalizationToken, _request: OutboxAdvanceRequest, _operation: OperationId, ) -> Result<OutboxPhase> { ... } fn get_outbox(&self, _key: OutboxKey) -> Result<Option<OutboxRecord>> { ... } fn list_outbox(&self) -> Result<Vec<OutboxRecord>> { ... } fn list_outbox_page( &self, _request: OutboxPageRequest, ) -> Result<OutboxPage> { ... } fn complete_outbox_effect( &self, _request: OutboxEffectCompletion, ) -> Result<()> { ... } fn complete_outbox_effect_with_operation( &self, request: OutboxEffectCompletion, _operation: OperationId, ) -> Result<()> { ... } fn create_replacement( &self, _effect: OutboxEffectKey, ) -> Result<ReplacementOutcome> { ... } fn create_replacement_with_operation( &self, effect: OutboxEffectKey, _operation: OperationId, ) -> Result<ReplacementOutcome> { ... } fn put_finalized_state(&self, _request: FinalizedStateRequest) -> Result<()> { ... } fn put_finalized_state_with_operation( &self, request: FinalizedStateRequest, _operation: OperationId, ) -> Result<()> { ... }
}
Expand description

Atomic ownership/lifecycle operations for a storage backend.

Implementations must keep the ownership check and the corresponding write in one transaction. A backend without this capability must leave Storage::trial_lifecycle at its default None rather than emulating these operations from raw CRUD.

State-bearing operations have one additional atomicity requirement. Before replacing a canonical typed state scope, an implementation must compare any existing blob’s kind and version inside the same transaction. A blob with a different kind or version is incompatible: return Error::Incompatible and leave the complete operation untouched. This applies to initial state in Self::claim, state mutations in Self::fenced_mutate, outbox state in Self::advance_outbox, replacements in Self::create_replacement, and terminal state in Self::put_finalized_state.

Required Methods§

fn reserve_and_create_if_budget( &self, request: ReserveAndCreateRequest, ) -> Result<ReservationOutcome>

Reserves and creates a waiting trial if the durable-record budget allows it. Created consumes a trial number; Exhausted consumes nothing.

§Errors

Returns Error::NotFound for an unknown study or Error::Storage for a backend failure.

fn abandon(&self, request: AbandonRequest) -> Result<bool>

Conditionally abandons an as-yet-unclaimed reservation.

Returns true when the reservation became Failed, and false when a concurrent claim or abandon already consumed it.

§Errors

Returns Error::NotFound for an unknown reservation and Error::Unsupported when the trial was not lifecycle-managed.

fn observe(&self, trial: TrialId) -> Result<LeaseObservation>

Reads the complete current lease observation for a trial.

§Errors

Returns Error::NotFound when the trial does not exist.

fn claim(&self, request: ClaimRequest) -> Result<TrialOwnerToken>

Atomically claims a reservation or paused trial, writes its initial parameters/state, and starts a new owner epoch.

§Errors

Returns Error::LeaseLost when the target is no longer claimable, or a validation/storage error without changing the trial.

fn renew(&self, request: RenewRequest) -> Result<LeaseObservation>

Renews a running owner and returns the new observation.

§Errors

Returns Error::LeaseLost for a stale token and leaves the trial untouched.

fn fail_stale(&self, request: FailStaleRequest) -> Result<bool>

Conditionally fails a running trial whose complete observed lease the caller proved unchanged for the request’s grace interval. Backends must compare the complete observation atomically, but must not compare the sweeper timestamp to the owner’s heartbeat timestamp: those clocks may belong to different nodes. Returns true for the sole race winner. The winner atomically commits the failed outcome and an Initial outbox record keyed by the observed trial and epoch, including the request’s finalization intent. A race loss commits neither.

§Errors

Returns Error::Unsupported when the trial is not lifecycle-managed; an observation mismatch is a normal Ok(false) race loss.

fn fenced_mutate(&self, request: FencedMutationRequest) -> Result<()>

Applies one mutation only if the owner/epoch token is still exact.

§Errors

Returns Error::LeaseLost for a stale token and performs no write.

fn terminal(&self, request: TerminalRequest) -> Result<()>

Validates and commits a terminal outcome only if the owner/epoch token is still exact.

§Errors

Returns Error::LeaseLost for a stale token or a validation error without partially committing the outcome.

Provided Methods§

fn observe_finalization( &self, _study: StudyId, ) -> Result<FinalizationObservation>

Reads the study-wide finalization ownership observation.

Observation is read-only: it neither creates the reserved claim state nor activates the ownership protocol. A missing claim is returned as the vacant (fence=0, heartbeat_seq=0, held=false) observation.

§Errors

Returns Error::Unsupported by default, or a backend error when the reserved claim state cannot be read or decoded.

fn acquire_finalization( &self, _request: FinalizationAcquireRequest, ) -> Result<FinalizationToken>

Atomically acquires or reclaims the study-wide finalization claim.

A held claim can be reclaimed only with an exact observation whose unchanged-grace proof was made by the caller. Backends must compare the observation and publish the replacement StateBlob in one lock or transaction; this API intentionally has no TTL or wall-clock expiry.

§Errors

Returns Error::Unsupported by default, typed backpressure for an ordinary contention, or a validation/storage error without changing the durable claim.

fn renew_finalization( &self, _token: FinalizationToken, ) -> Result<FinalizationObservation>

Atomically renews a held study-wide finalization claim.

§Errors

Returns Error::Unsupported by default or Error::LeaseLost for a stale/released token.

fn release_finalization(&self, _token: FinalizationToken) -> Result<()>

Atomically releases a held study-wide finalization claim.

Release preserves the fence and permanent protocol activation. It does not make legacy canonical state writes safe again.

§Errors

Returns Error::Unsupported by default or Error::LeaseLost for a stale/released token.

fn terminal_with_intent( &self, _request: TerminalRequest, _intent: FinalizationIntent, ) -> Result<OutboxKey>

Commits a terminal outcome together with its durable initial outbox intent.

The terminal transition and the creation of the outbox record are one capability operation. A backend that cannot provide this atomic boundary must return Unsupported rather than issue a terminal write followed by a second outbox write.

The default keeps older lifecycle implementations source-compatible; such an implementation must opt into this method before it can pass the outbox conformance suite.

§Errors

Returns the unsupported error by default. Implementations also return a lease-lost error for a stale token or a validation/storage error without changing the terminal trial.

fn terminal_with_intent_owned( &self, _owner: FinalizationToken, _request: TerminalRequest, _intent: FinalizationIntent, ) -> Result<OutboxKey>

Commits a terminal result and initial outbox intent under a study-wide finalization owner.

The owner check, terminal transition, seam-state replacement, and outbox insertion must use one backend lock or transaction. A terminal intent with no seam-state replacement may still be admitted by a backend while another finalizer owns the study; stateful finalization must use the exact active owner token.

The default is unsupported so older implementations remain source compatible while callers fail closed rather than emulating ownership with raw CRUD.

§Errors

Returns Error::Unsupported by default. Implementations also return Error::LeaseLost for a stale owner or a validation/storage error. Ownership and validation rejection publish no mutation; a transport failure can leave the commit outcome unknown.

fn advance_outbox(&self, _request: OutboxAdvanceRequest) -> Result<OutboxPhase>

Atomically persists callback state and discovered intents while moving one outbox record through its explicit phases.

Replaying an already advanced phase is a no-op. Skipping a phase is a conflict. The returned phase is the durable cursor; an Effects phase remains current until all discovered effects are complete.

§Errors

Returns the unsupported error by default, a not-found error for an unknown outbox, or a conflict when a phase is skipped or an effect replay payload differs. State blobs supplied for the current phase replace the persisted seam state before the phase advances.

fn advance_outbox_with_operation( &self, request: OutboxAdvanceRequest, _operation: OperationId, ) -> Result<OutboxPhase>

Advances one outbox phase with the caller’s lifecycle operation correlation.

The operation is context, not durable outbox identity: replay safety still comes from OutboxKey. OperationId::NONE is honest for recovery that has no live caller. The default delegates to Self::advance_outbox so existing third-party implementations remain source-compatible; implementations that record backend request correlation can override this method.

§Errors

Returns the same error as Self::advance_outbox.

fn mutate_owned( &self, _owner: FinalizationToken, _request: FencedMutationRequest, ) -> Result<()>

Applies one lifecycle mutation under a study-wide finalization owner.

This is the finalization counterpart to Self::fenced_mutate. It returns the same unit result and requires the backend to validate the owner and apply the existing mutation plan atomically.

§Errors

Returns Error::Unsupported by default. Implementations also return Error::LeaseLost for a stale owner or a validation/storage error. Ownership and validation rejection publish no mutation; a transport failure can leave the commit outcome unknown.

fn advance_outbox_owned( &self, _owner: FinalizationToken, _request: OutboxAdvanceRequest, _operation: OperationId, ) -> Result<OutboxPhase>

Advances one outbox phase under a study-wide finalization owner.

The owner check and phase/state write share the backend’s existing lifecycle lock or transaction. The operation id is correlation context only and does not change outbox identity.

§Errors

Returns Error::Unsupported by default. Implementations also return Error::LeaseLost for a stale owner or the same not-found, conflict, and storage errors as Self::advance_outbox.

fn get_outbox(&self, _key: OutboxKey) -> Result<Option<OutboxRecord>>

Reads one durable outbox record, including its replay cursor and completed effect keys.

§Errors

Returns the unsupported error by default or a backend storage error.

fn list_outbox(&self) -> Result<Vec<OutboxRecord>>

Lists durable outbox records in key order, including fully-done records.

§Errors

Returns the unsupported error by default or a backend storage error.

fn list_outbox_page(&self, _request: OutboxPageRequest) -> Result<OutboxPage>

Reads one bounded page of study-scoped durable outbox records.

The default is deliberately unsupported. A backend that only implements Self::list_outbox must not claim bounded traversal by materializing the complete history and trimming it afterwards. Implementations return records in immutable terminal-sequence and OutboxKey order, include done records, and preserve an explicit stateless continuation when the limits stop before the request’s history horizon.

§Errors

Returns Error::ResourceLimit for a zero or unrepresentable limit, or a record that cannot fit the byte budget, Error::Conflict for a foreign, missing, or inconsistent cursor, and Error::Unsupported when the backend has no bounded-page capability.

fn complete_outbox_effect(&self, _request: OutboxEffectCompletion) -> Result<()>

Idempotently acknowledges an externally executed effect.

§Errors

Returns the unsupported error by default, a not-found error for an unknown effect, or a conflict when the effect is not externally acknowledgeable.

fn complete_outbox_effect_with_operation( &self, request: OutboxEffectCompletion, _operation: OperationId, ) -> Result<()>

Acknowledges one externally executed effect with caller correlation.

The effect key remains the sole idempotency identity. The default delegates to Self::complete_outbox_effect for source-compatible implementations; OperationId::NONE means no live caller (usually recovery).

§Errors

Returns the same error as Self::complete_outbox_effect.

fn create_replacement( &self, _effect: OutboxEffectKey, ) -> Result<ReplacementOutcome>

Idempotently creates the replacement described by a durable effect.

The effect key, rather than a caller-generated trial id, is the idempotency key. A crash after creation and before the caller receives the result therefore returns the already-created result on replay.

§Errors

Returns the unsupported error by default, a not-found error for an unknown effect, or a validation/storage error while creating the replacement.

fn create_replacement_with_operation( &self, effect: OutboxEffectKey, _operation: OperationId, ) -> Result<ReplacementOutcome>

Creates one replacement with caller correlation.

The effect key remains the sole idempotency identity. The default delegates to Self::create_replacement for source-compatible implementations; OperationId::NONE means no live caller (usually recovery).

§Errors

Returns the same error as Self::create_replacement.

fn put_finalized_state(&self, _request: FinalizedStateRequest) -> Result<()>

Replaces one state blob on a terminal lifecycle-managed trial.

The operation has no owner token by design, but it still requires a known terminal trial and an exact trial-owned scope. Canonical constraint state must use the arity-checked fenced operation instead. Repeating the same write is a no-op; a different blob replaces the old finalized value atomically.

§Errors

Returns the unsupported error by default, a not-found error for an unknown trial, or a conflict for a nonterminal trial, a foreign scope, or canonical constraint state.

fn put_finalized_state_with_operation( &self, request: FinalizedStateRequest, _operation: OperationId, ) -> Result<()>

Replaces finalized state with caller correlation.

The operation is request context and is not persisted with the state blob. The default delegates to Self::put_finalized_state for source-compatible implementations; OperationId::NONE means no caller operation is available.

§Errors

Returns the same error as Self::put_finalized_state.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§