Skip to main content

Backoff

Struct Backoff 

Source
pub struct Backoff { /* private fields */ }
Expand description

A retry backoff policy.

Backoff is a small, Copy value. Construct one with Backoff::constant, Backoff::linear, or Backoff::exponential, then optionally cap it with Backoff::with_max_delay and Backoff::with_max_retries.

Backoff::delay takes a zero-based attempt number (attempt 0 is the wait before the first retry) and returns the delay, or None when the retry limit has been reached.

Implementations§

Source§

impl Backoff

Source

pub const fn constant(base: Duration) -> Self

A constant backoff that always waits base.

Source

pub const fn linear(base: Duration, step: Duration) -> Self

A linear backoff: attempt n waits base + step * n.

Source

pub const fn exponential(base: Duration, factor: u32) -> Self

An exponential backoff: attempt n waits base * factor^n.

factor is the integer multiplier (e.g. 2 doubles each attempt). A factor below 2 makes the policy behave like Backoff::constant.

Examples found in repository?
examples/basic.rs (line 16)
14fn main() {
15    // 100ms base, double each attempt, capped at 2s, up to 5 retries.
16    let policy = Backoff::exponential(Duration::from_millis(100), 2)
17        .with_max_delay(Duration::from_secs(2))
18        .with_max_retries(5);
19
20    // A tiny deterministic PRNG stands in for a real RNG so the example output
21    // is reproducible. In real code use `rand`, `getrandom`, or a hardware RNG.
22    let mut seed: u32 = 0x9e37_79b9;
23    let mut next_rand = move || {
24        seed ^= seed << 13;
25        seed ^= seed >> 17;
26        seed ^= seed << 5;
27        seed
28    };
29
30    println!("attempt  base-delay  jittered-delay");
31    for (attempt, base) in policy.delays().enumerate() {
32        let jittered = full_jitter(base, next_rand());
33        println!("{attempt:>7}  {base:>10?}  {jittered:>14?}");
34        // In a real loop you would: sleep(jittered); if try_operation().is_ok() { break; }
35    }
36
37    println!("\nretry limit reached; giving up");
38}
Source

pub const fn with_max_delay(self, max_delay: Duration) -> Self

Caps every computed delay at max_delay.

Examples found in repository?
examples/basic.rs (line 17)
14fn main() {
15    // 100ms base, double each attempt, capped at 2s, up to 5 retries.
16    let policy = Backoff::exponential(Duration::from_millis(100), 2)
17        .with_max_delay(Duration::from_secs(2))
18        .with_max_retries(5);
19
20    // A tiny deterministic PRNG stands in for a real RNG so the example output
21    // is reproducible. In real code use `rand`, `getrandom`, or a hardware RNG.
22    let mut seed: u32 = 0x9e37_79b9;
23    let mut next_rand = move || {
24        seed ^= seed << 13;
25        seed ^= seed >> 17;
26        seed ^= seed << 5;
27        seed
28    };
29
30    println!("attempt  base-delay  jittered-delay");
31    for (attempt, base) in policy.delays().enumerate() {
32        let jittered = full_jitter(base, next_rand());
33        println!("{attempt:>7}  {base:>10?}  {jittered:>14?}");
34        // In a real loop you would: sleep(jittered); if try_operation().is_ok() { break; }
35    }
36
37    println!("\nretry limit reached; giving up");
38}
Source

pub const fn with_max_retries(self, max_retries: u32) -> Self

Limits the policy to at most max_retries retries.

After this, delay returns None for attempt numbers >= max_retries.

Examples found in repository?
examples/basic.rs (line 18)
14fn main() {
15    // 100ms base, double each attempt, capped at 2s, up to 5 retries.
16    let policy = Backoff::exponential(Duration::from_millis(100), 2)
17        .with_max_delay(Duration::from_secs(2))
18        .with_max_retries(5);
19
20    // A tiny deterministic PRNG stands in for a real RNG so the example output
21    // is reproducible. In real code use `rand`, `getrandom`, or a hardware RNG.
22    let mut seed: u32 = 0x9e37_79b9;
23    let mut next_rand = move || {
24        seed ^= seed << 13;
25        seed ^= seed >> 17;
26        seed ^= seed << 5;
27        seed
28    };
29
30    println!("attempt  base-delay  jittered-delay");
31    for (attempt, base) in policy.delays().enumerate() {
32        let jittered = full_jitter(base, next_rand());
33        println!("{attempt:>7}  {base:>10?}  {jittered:>14?}");
34        // In a real loop you would: sleep(jittered); if try_operation().is_ok() { break; }
35    }
36
37    println!("\nretry limit reached; giving up");
38}
Source

pub const fn base(&self) -> Duration

Returns the base delay.

Source

pub const fn max_delay(&self) -> Duration

Returns the maximum delay cap.

Source

pub const fn max_retries(&self) -> Option<u32>

Returns the retry limit, if any.

Source

pub fn delay(&self, attempt: u32) -> Option<Duration>

Returns the delay to wait before retry attempt (zero-based), or None if the retry limit has been reached.

The result is always clamped to max_delay. All arithmetic saturates and the computation runs in bounded time, so large attempt numbers never overflow, panic, or hang.

Source

pub const fn delays(&self) -> Delays

Returns an iterator over the delays for attempts 0, 1, 2, ....

The iterator ends when the retry limit is reached. With no retry limit it is infinite.

Examples found in repository?
examples/basic.rs (line 31)
14fn main() {
15    // 100ms base, double each attempt, capped at 2s, up to 5 retries.
16    let policy = Backoff::exponential(Duration::from_millis(100), 2)
17        .with_max_delay(Duration::from_secs(2))
18        .with_max_retries(5);
19
20    // A tiny deterministic PRNG stands in for a real RNG so the example output
21    // is reproducible. In real code use `rand`, `getrandom`, or a hardware RNG.
22    let mut seed: u32 = 0x9e37_79b9;
23    let mut next_rand = move || {
24        seed ^= seed << 13;
25        seed ^= seed >> 17;
26        seed ^= seed << 5;
27        seed
28    };
29
30    println!("attempt  base-delay  jittered-delay");
31    for (attempt, base) in policy.delays().enumerate() {
32        let jittered = full_jitter(base, next_rand());
33        println!("{attempt:>7}  {base:>10?}  {jittered:>14?}");
34        // In a real loop you would: sleep(jittered); if try_operation().is_ok() { break; }
35    }
36
37    println!("\nretry limit reached; giving up");
38}

Trait Implementations§

Source§

impl Clone for Backoff

Source§

fn clone(&self) -> Backoff

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Backoff

Source§

impl Debug for Backoff

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for Backoff

Source§

impl Hash for Backoff

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for Backoff

Source§

fn eq(&self, other: &Backoff) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for Backoff

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.