backoff

Retry Rust operations with exponential backoff, for both sync and async code

Library
Cargo
v0.4.0
343stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
36/100Needs Attention
Development Activity0
Maintenance0
Community64
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
71/100Good
Architecture72
Code Quality70
Innovation60
Learning Curve80

backoff is a small Rust crate for retrying fallible operations according to a configurable backoff policy, most commonly exponential backoff with jitter. It wraps errors as either transient (retryable) or permanent (stop immediately), and ships retry/retry_notify helpers for synchronous closures plus an async-aware backoff::future::retry for use with tokio, async-std, or any Future-based executor.

Inspired by Google’s google-http-java-client retry mechanism and its Go port, the crate keeps its API surface intentionally narrow: a Backoff trait, a handful of built-in policies (ExponentialBackoff, Zero, Stop, Constant), and free functions to drive retries — no macros, no derive, no framework lock-in.

What You Get

  • ExponentialBackoff with configurable randomization_factor, multiplier, and max elapsed time
  • Error::transient() / Error::permanent() wrapping to control whether a failure is retried
  • Sync retry()/retry_notify() free functions for blocking closures
  • Async backoff::future::retry() supporting tokio, async-std, or generic futures via feature flags
  • WASM support via the wasm-bindgen feature flag

Common Use Cases

  • Retrying flaky HTTP requests (e.g. wrapping reqwest calls) with exponential backoff and jitter
  • Distinguishing transient failures (network blips, 429/503 responses) from permanent ones (4xx client errors) so only the former are retried
  • Adding retry logic to async database or API clients built on tokio or async-std
  • Bounding retry attempts with a maximum elapsed time so operations fail fast instead of retrying forever

Under The Hood

Architecture — The crate centers on a Backoff trait (src/backoff.rs) with next_backoff()/reset(), implemented by ExponentialBackoff (src/exponential.rs, the main policy with jitter/multiplier/max-elapsed-time logic), plus trivial Zero/Stop policies. Errors are wrapped by an Error<E> enum (src/error.rs) distinguishing Transient (optionally carrying a retry_after duration for rate-limit responses) from Permanent. src/retry.rs implements the blocking retry loop, sleeping via std::thread::sleep, while src/future.rs implements the async equivalent using pin-project-lite to poll the operation future and a timer future without unsafe pinning code, gated behind futures/tokio/async-std feature flags. src/clock.rs abstracts wall-clock access (via the instant crate) so time can be mocked in tests.

Tech Stack — Pure Rust (2018 edition), depending on rand/getrandom for jitter, instant for WASM-compatible time, and optionally tokio, async-std, futures-core, and pin-project-lite behind feature flags; dev-dependencies include reqwest and tokio for the documented examples.

Code Qualitytests/exponential.rs and tests/retry.rs cover the exponential backoff policy’s timing math and the retry loop’s transient/permanent branching; three runnable examples (examples/retry.rs, examples/async.rs, examples/permanent_error.rs) double as living documentation. The crate is compact (~1,200 lines across 8 modules) and each module has a single clear responsibility, though the repo has seen no commits since February 2024 and the crates.io release is still tagged 0.4.1-alpha.0, indicating maintenance has slowed.

API Design — The public surface is a handful of free functions (retry, retry_notify) plus one trait, and adopting it requires only wrapping a closure’s error type in Error::transient()/Error::permanent() — minimal boilerplate. Feature-gating keeps the dependency footprint small for consumers who only need the sync path, at the cost of needing to know which Cargo feature (tokio, async-std, futures) to enable for the async variant.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search