Qubit Retry
Qubit Retry provides type-preserving retry executors for Rust sync and async operations.
The core API is RetryExecutor<E>. An executor is bound only to the operation error type E; the success type T is introduced by run or run_async. This means normal error retry does not require T: Clone + Eq + Hash.
Features
- Type-preserving
RetryError<E>that keeps the original operation error. - Sync retry via
RetryExecutor::run. - Async retry via
RetryExecutor::run_async. - Real async per-attempt timeout via
RetryExecutor::run_async_with_timeout. - Delay strategies:
Delay::none,Delay::fixed,Delay::random,Delay::exponential. - Symmetric jitter through
Jitter::factor. - Explicit retry classification with
retry_iforclassify_error. - Listener contexts for retry/failure/abort plus borrowed failure payloads.
- Listener callback storage based on
qubit-functionfunctors (ArcConsumer/ArcBiConsumer). - Immutable
RetryOptionssnapshots withqubit-configintegration.
Core Concepts
qubit-retry is designed around a type-preserving retry executor with clear boundaries between retry policy, error classification, and operation execution:
RetryExecutor<E>stores retry behavior and error classification.run<T, _>andrun_async<T, _, _>introduce the success type only at execution time.- Listener callbacks observe context metadata and borrowed failures instead of owned success values.
RetryOptionsprovides a validated immutable snapshot for retry configuration.
Installation
[]
= "0.3.0"
Basic Sync Retry
use ;
use Duration;
Error Classification
By default, all operation errors are retryable until the attempt or elapsed-time limit is reached. Use retry_if when only some errors should be retried:
use ;
use Duration;
let executor = builder
.max_attempts
.delay
.retry_if
.build?;
Use classify_error when the classifier needs to return a named decision:
use ;
let executor = builder
.max_attempts
.classify_error
.build?;
Async Retry and Attempt Timeout
run_async_with_timeout uses tokio::time::timeout, so timed-out attempts are actually cancelled at the future boundary.
use ;
use Duration;
async
async
Use run_async when you do not need a per-attempt timeout:
let response = executor
.run_async
.await?;
Listeners
Retry/failure/abort listeners receive a context object plus a borrowed failure payload. Success listeners still receive only SuccessContext.
pub type RetryListener<E> = ;
pub type FailureListener<E> = ;
pub type AbortListener<E> = ;
pub type SuccessListener = ;
use ;
use Duration;
let executor = builder
.max_attempts
.delay
.on_retry
.on_failure
.on_abort
.on_success
.build?;
Configuration
RetryOptions is an immutable snapshot. Reading from qubit-config happens once during construction.
use Config;
use ;
let mut config = new;
config.set?;
config.set?;
config.set?;
config.set?;
config.set?;
config.set?;
config.set?;
let options = from_config?;
let executor = from_options?;
Supported relative keys:
max_attemptsmax_elapsed_millisdelay:none,fixed,random,exponential, orexponential_backofffixed_delay_millisrandom_min_delay_millisrandom_max_delay_millisexponential_initial_delay_millisexponential_max_delay_millisexponential_multiplierjitter_factor
Error Handling
RetryError<E> preserves the original operation error when the terminal failure is an application error:
use ;
let executor = builder
.max_attempts
.build?;
match executor.run