Expand description
§sisyphus
A generic, execution-agnostic and time-agnostic backoff & retry utility, built as a pure state machine.
Sisyphus was condemned by the gods to roll a boulder up a hill for eternity, only to watch it roll back down every time — the original retry loop. This crate is the part of that punishment worth keeping: it computes when to push the boulder again, and nothing else.
§Why another retry crate?
Most retry crates (e.g. backoff) bake in std::time::Instant and
std::thread::sleep, coupling policy (how long to wait) with execution
(how to wait). That falls apart in:
- WebAssembly engines executing Wasm directly, with no host threads;
- Deterministic blockchain / consensus state machines, where wall-clock reads and panics are forbidden;
no_stdembedded targets with no allocator and nostd::time.
sisyphus solves this by being a pure state machine:
#![no_std], zero allocation, nodyn(unless you opt in).- Time is just
core::time::Duration; instants come from yourClock. - Randomness/jitter is injected via
Jitter(deterministic by default). - Execution is driven by your
sleep— sync closure or async future.
§30-second tour
use core::ops::ControlFlow;
use core::time::Duration;
use sisyphus::{retry_sync, ExponentialBackoff, PolicyExt, RetryError};
// 1. Build a policy by composing pure state machines.
let policy = ExponentialBackoff::new(Duration::from_millis(50), 2.0)
.with_max_delay(Duration::from_secs(5))
.max_attempts(4);
// 2. Drive it with your own operation + sleep. Here `sleep` just advances
// a virtual clock, so the example runs instantly and deterministically.
let mut tries = 0u32;
let mut elapsed = Duration::ZERO;
let result: Result<&str, RetryError<&str>> = retry_sync(
policy,
|| {
tries += 1;
if tries >= 3 { ControlFlow::Break("connected") }
else { ControlFlow::Continue("connection refused") }
},
|delay| elapsed += delay, // plug in your real sleep in production
);
assert_eq!(result, Ok("connected"));§Feature flags
| feature | default | adds |
|---|---|---|
alloc | no | BoxedPolicy, impl BackoffPolicy for Box<dyn …> |
std | no | SystemClock backed by std::time::Instant |
The default build pulls in neither, keeping it pure-core and
allocation-free.
Structs§
- Constant
- A policy that always returns the same fixed delay, forever.
- Exponential
Backoff - Classic exponential backoff with optional injected jitter.
- MaxAttempts
- Caps the total number of delays an inner policy may produce.
- MaxElapsed
Time - Stops retrying once a wall-clock budget has elapsed.
- NoJitter
- A
Jitterthat adds no randomness at all. - Split
Mix64 - A tiny, fast, deterministic PRNG (Steele et al.’s SplitMix64).
- System
Clock std - A monotonic
Clockbacked bystd::time::Instant. - With
MaxDelay - Caps the maximum delay returned by an inner policy.
Enums§
- Retry
Error - The reason a retry loop stopped without a terminal
ControlFlow::Break.
Traits§
- Backoff
Policy - The core retry-timing state machine.
- Clock
- A source of monotonic time supplied by the host environment.
- Jitter
- A source of uniform randomness used to perturb backoff delays.
- Policy
Ext - Ergonomic, zero-cost combinators for any
BackoffPolicy.
Functions§
- retry_
async - Drive an operation to completion asynchronously, backing off between tries.
- retry_
sync - Drive an operation to completion synchronously, backing off between tries.
Type Aliases§
- Boxed
Policy alloc - A heap-allocated, type-erased
BackoffPolicy.