mailrs-backoff 1.0.0

Exponential backoff with optional jitter (AWS-style taxonomy: None / Equal / Full). Pure delay math — no I/O, no async, no allocation. Useful for any retry loop: SMTP outbound, webhook delivery, auth-lockout, HTTP client retries.
Documentation
  • Coverage
  • 100%
    17 out of 17 items documented1 out of 10 items with examples
  • Size
  • Source code size: 54.67 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 419.33 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 6s Average build duration of successful builds.
  • all releases: 4s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • goliajp/mailrs
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • doracawl

mailrs-backoff

Crates.io docs.rs License

Exponential backoff with optional jitter. Pure delay math — no I/O, no async, no RNG dependency. Useful for any retry loop: SMTP outbound delivery, webhook re-delivery, auth-lockout penalty, HTTP client retries, …

Follows AWS Architecture Blog's Exponential Backoff and Jitter taxonomy:

  • None — deterministic, simple, but causes thundering-herd at scale
  • Equal — half fixed, half random, bounded smoothing
  • Full — uniform random [0, base], AWS's recommended default

Quickstart

use mailrs_backoff::{Backoff, Jitter};
use std::time::Duration;

// Use a preset:
let b = Backoff::smtp_outbound();
// Or roll your own:
let b = Backoff::new(
    Duration::from_secs(1),
    2.0,                            // doubling
    Duration::from_secs(300),       // cap at 5 minutes
    Jitter::Full,
);

let seed = rand_seed();
for attempt in 0..b_max_attempts() {
    let delay = b.delay(attempt, seed);
    // ... sleep then retry ...
    if Backoff::should_give_up(attempt + 1, 10) {
        break;
    }
}
# fn rand_seed() -> u64 { 0 }
# fn b_max_attempts() -> u32 { 10 }

Why bring your own seed?

This crate has zero runtime dependencies and no RNG state of its own. That means:

  • No transitive pull-in of rand, getrandom, wasi-libc, etc.
  • Deterministic tests — pass the same seed, get the same delay
  • You control the entropy source (cryptographic if you need it, cheap if you don't)

Typical seed source:

// Cheap, non-crypto — fine for jitter purposes:
let seed = std::time::Instant::now().elapsed().as_nanos() as u64;
// Or, if you already have rand in your deps:
// let seed = rand::random::<u64>();

For Jitter::None, the seed is ignored entirely.

Presets

Preset initial multiplier max jitter
smtp_outbound 60s 2.5 8h Full
auth_lockout 30min 2.0 24h None
webhook 60s 2.0 6h Equal

These match (or extend with jitter) the policies used inside mailrs-* crates — see workspace comments. Pick a preset or tune your own; the struct fields are all pub.

What this crate does

  • Backoff::base_delay(attempt)min(initial × multiplier^attempt, max), pure exponential without jitter. Useful for logging the "scheduled" delay alongside the actual jittered one.
  • Backoff::delay(attempt, seed) — base delay with jitter applied per the configured Jitter policy.
  • Backoff::should_give_up(attempt, max) — convenience predicate for retry loops.
  • Three presets: smtp_outbound, auth_lockout, webhook.

What this crate does not

  • No async sleep helper. You sleep with tokio::time::sleep / std::thread::sleep / whatever your runtime gives you.
  • No retry loop runner. This crate computes durations; the loop is yours.
  • No RNG. Bring your own seed.
  • No "cap on total elapsed". If you want "give up after 30 minutes of cumulative retries," wrap with your own deadline check — one extra if Instant::now() > deadline { break } line.

Performance

Measured (criterion, M-series Mac, release, 100-sample median):

Operation Median
base_delay(attempt=3) ~8 ns
delay(attempt=3, Jitter::None) ~23 ns
delay(attempt=3, Jitter::Equal) ~31 ns
delay(attempt=3, Jitter::Full) ~11 ns
delay(attempt=100, capped) ~10 ns
should_give_up <1 ns

Reproduce: cargo bench -p mailrs-backoff --bench backoff. Workspace PERFORMANCE.md carries the same table.

License

Apache-2.0 OR MIT.