Crate backoff[][src]

ExponentialBackoff is a backoff implementation that increases the backoff period for each retry attempt using a randomization function that grows exponentially.

next_backoff is calculated using the following formula:

  randomized interval =
      retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])

In other words next_backoff will range between the randomization factor percentage below and above the retry interval.

For example, given the following parameters:

retry_interval = 2
randomization_factor = 0.5
multiplier = 2

the actual backoff period used in the next retry attempt will range between 1 and 3 seconds, multiplied by the exponential, that is, between 2 and 6 seconds.

Note: max_interval caps the retry_interval and not the randomized interval.

If the time elapsed since an ExponentialBackoff instance is created goes past the max_elapsed_time, then the method next_backoff starts returning None.

The elapsed time can be reset by calling reset.

Example: Given the following default arguments, for 10 tries the sequence will be, and assuming we go over the max_elapsed_time on the 10th try:

Request #retry_interval (seconds)Randomized Interval (seconds)
10.5[0.25, 0.75]
20.75[0.375, 1.125]
31.125[0.562, 1.687]
41.687[0.8435, 2.53]
52.53[1.265, 3.795]
63.795[1.897, 5.692]
75.692[2.846, 8.538]
88.538[4.269, 12.807]
912.807[6.403, 19.210]
1019.210None

Examples

Permanent errors

Permanent errors are not retried. You have to wrap your error value explicitly into Error::Permanent. You can use Result's map_err method.

examples/permanent_error.rs:

use backoff::{Error, ExponentialBackoff};
use reqwest::Url;

use std::fmt::Display;
use std::io::{self, Read};

fn new_io_err<E: Display>(err: E) -> io::Error {
    io::Error::new(io::ErrorKind::Other, err.to_string())
}

fn fetch_url(url: &str) -> Result<String, Error<io::Error>> {
    let op = || {
        println!("Fetching {}", url);
        let url = Url::parse(url)
            .map_err(new_io_err)
            // Permanent errors need to be explicitly constructed.
            .map_err(Error::Permanent)?;

        let mut resp = reqwest::blocking::get(url)
            // Transient errors can be constructed with the ? operator
            // or with the try! macro. No explicit conversion needed
            // from E: Error to backoff::Error;
            .map_err(new_io_err)?;

        let mut content = String::new();
        let _ = resp.read_to_string(&mut content);
        Ok(content)
    };

    let backoff = ExponentialBackoff::default();
    backoff::retry(backoff, op)
}

fn main() {
    match fetch_url("https::///wrong URL") {
        Ok(_) => println!("Successfully fetched"),
        Err(err) => panic!("Failed to fetch: {}", err),
    }
}

Transient errors

Transient errors can be constructed by wrapping your error value into Error::Transient. By using the ? operator or the try! macro, you always get transient errors.

examples/retry.rs:

use backoff::{retry, Error, ExponentialBackoff};

use std::io::Read;

fn fetch_url(url: &str) -> Result<String, Error<reqwest::Error>> {
    let mut op = || {
        println!("Fetching {}", url);
        let mut resp = reqwest::blocking::get(url)?;

        let mut content = String::new();
        let _ = resp.read_to_string(&mut content);
        Ok(content)
    };

    let backoff = ExponentialBackoff::default();
    retry(backoff, op)
}

fn main() {
    match fetch_url("https://www.rust-lang.org") {
        Ok(_) => println!("Sucessfully fetched"),
        Err(err) => panic!("Failed to fetch: {}", err),
    }
}

Output with internet connection:

$ time cargo run --example retry
   Compiling backoff v0.1.0 (file:///home/tibi/workspace/backoff)
    Finished dev [unoptimized + debuginfo] target(s) in 1.54 secs
     Running `target/debug/examples/retry`
Fetching https://www.rust-lang.org
Sucessfully fetched

real    0m2.003s
user    0m1.536s
sys    0m0.184s

Output without internet connection

$ time cargo run --example retry
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/examples/retry`
Fetching https://www.rust-lang.org
Fetching https://www.rust-lang.org
Fetching https://www.rust-lang.org
Fetching https://www.rust-lang.org
^C

real    0m2.826s
user    0m0.008s
sys    0m0.000s

Async

Please set either the tokio or async-std features in Cargo.toml to enable the async support of this library, i.e.:

backoff = { version = "x.y.z", features = ["tokio"] }

A Future<Output = Result<T, backoff::Error<E>> can be easily retried:

examples/async.rs:


extern crate tokio_1 as tokio;

use backoff::ExponentialBackoff;

async fn fetch_url(url: &str) -> Result<String, reqwest::Error> {
    backoff::future::retry(ExponentialBackoff::default(), || async {
        println!("Fetching {}", url);
        Ok(reqwest::get(url).await?.text().await?)
    })
    .await
}

#[tokio::main]
async fn main() {
    match fetch_url("https://www.rust-lang.org").await {
        Ok(_) => println!("Successfully fetched"),
        Err(err) => panic!("Failed to fetch: {}", err),
    }
}

Feature flags

  • futures: enables futures support,
  • tokio: enables support for the tokio async runtime, implies futures,
  • async-std: enables support for the async-std async runtime, implies futures,
  • wasm-bindgen: enabled support for wasm-bindgen.

Modules

backoff
default

Constants for the exponential backoff policy.

exponential
futurefutures

Structs

SystemClock

SystemClock uses the system's clock to get the current time. This Clock should be used for real use-cases.

Enums

Error

Error is the error value in an operation's result.

Traits

Clock

Clock returns the current time.

Notify

Notify is called in retry_notify in case of errors.

Functions

retry

Retries this operation according to the backoff policy. backoff is reset before it is used.

retry_notify

Retries this operation according to the backoff policy. Calls notify on failed attempts (in case of transient errors). backoff is reset before it is used.

Type Definitions

ExponentialBackoff

Exponential backoff policy with system's clock.