Skip to main content

PolicyExt

Trait PolicyExt 

Source
pub trait PolicyExt: BackoffPolicy + Sized {
    // Provided methods
    fn max_attempts(self, max: u32) -> MaxAttempts<Self> { ... }
    fn with_max_delay(self, max_delay: Duration) -> WithMaxDelay<Self> { ... }
    fn max_elapsed_time<C: Clock>(
        self,
        clock: C,
        max_elapsed: Duration,
    ) -> MaxElapsedTime<Self, C> { ... }
}
Expand description

Ergonomic, zero-cost combinators for any BackoffPolicy.

Blanket-implemented for every policy, so you can fluently layer caps:

use core::time::Duration;
use sisyphus::{Constant, PolicyExt};

let policy = Constant::new(Duration::from_millis(100))
    .with_max_delay(Duration::from_secs(1))
    .max_attempts(3);

Provided Methods§

Source

fn max_attempts(self, max: u32) -> MaxAttempts<Self>

Cap the number of retries to max. See MaxAttempts.

This counts retries, not total attempts: the initial attempt is always made before any delay, so the operation runs up to max + 1 times.

Examples found in repository?
examples/async_retry.rs (line 27)
26async fn run() -> Result<u32, RetryError<&'static str>> {
27    let policy = ExponentialBackoff::new(Duration::from_millis(10), 2.0).max_attempts(5);
28    let mut attempt = 0u32;
29
30    retry_async(
31        policy,
32        || {
33            attempt += 1;
34            async move {
35                println!("async attempt {attempt}");
36                if attempt < 4 {
37                    ControlFlow::Continue("service unavailable")
38                } else {
39                    ControlFlow::Break(attempt)
40                }
41            }
42        },
43        // Swap this closure for your runtime's real sleep in production.
44        fake_sleep,
45    )
46    .await
47}
More examples
Hide additional examples
examples/quick_start.rs (line 28)
25fn main() {
26    let policy = ExponentialBackoff::new(Duration::from_millis(50), 2.0)
27        .with_max_delay(Duration::from_secs(1))
28        .max_attempts(6);
29
30    let mut attempt = 0u32;
31    let started = Instant::now();
32
33    let result: Result<&str, RetryError<&str>> = retry_sync(
34        policy,
35        || {
36            attempt += 1;
37            match flaky_connect(attempt) {
38                Ok(ok) => ControlFlow::Break(ok),
39                Err(transient) => {
40                    println!("attempt {attempt} failed: {transient}");
41                    ControlFlow::Continue(transient)
42                }
43            }
44        },
45        // The execution side is entirely yours; here we block the OS thread.
46        |delay| {
47            println!("  backing off for {delay:?}");
48            std::thread::sleep(delay);
49        },
50    );
51
52    match result {
53        Ok(msg) => println!(
54            "success after {attempt} attempts in {:?}: {msg}",
55            started.elapsed()
56        ),
57        Err(RetryError::Exhausted(last)) => {
58            println!("gave up after {attempt} attempts; last error: {last}");
59        }
60    }
61}
Source

fn with_max_delay(self, max_delay: Duration) -> WithMaxDelay<Self>

Clamp every produced delay to at most max_delay. See WithMaxDelay.

Examples found in repository?
examples/quick_start.rs (line 27)
25fn main() {
26    let policy = ExponentialBackoff::new(Duration::from_millis(50), 2.0)
27        .with_max_delay(Duration::from_secs(1))
28        .max_attempts(6);
29
30    let mut attempt = 0u32;
31    let started = Instant::now();
32
33    let result: Result<&str, RetryError<&str>> = retry_sync(
34        policy,
35        || {
36            attempt += 1;
37            match flaky_connect(attempt) {
38                Ok(ok) => ControlFlow::Break(ok),
39                Err(transient) => {
40                    println!("attempt {attempt} failed: {transient}");
41                    ControlFlow::Continue(transient)
42                }
43            }
44        },
45        // The execution side is entirely yours; here we block the OS thread.
46        |delay| {
47            println!("  backing off for {delay:?}");
48            std::thread::sleep(delay);
49        },
50    );
51
52    match result {
53        Ok(msg) => println!(
54            "success after {attempt} attempts in {:?}: {msg}",
55            started.elapsed()
56        ),
57        Err(RetryError::Exhausted(last)) => {
58            println!("gave up after {attempt} attempts; last error: {last}");
59        }
60    }
61}
Source

fn max_elapsed_time<C: Clock>( self, clock: C, max_elapsed: Duration, ) -> MaxElapsedTime<Self, C>

Stop retrying after max_elapsed measured by clock. See MaxElapsedTime.

Examples found in repository?
examples/system_clock.rs (line 18)
15fn main() {
16    // Poll every 20ms, but never spend more than 200ms of real time trying.
17    let policy = Constant::new(Duration::from_millis(20))
18        .max_elapsed_time(SystemClock, Duration::from_millis(200));
19
20    let mut attempt = 0u32;
21    let result: Result<(), RetryError<u32>> = retry_sync(
22        policy,
23        || {
24            attempt += 1;
25            ControlFlow::Continue(attempt) // never succeeds; rely on the budget
26        },
27        std::thread::sleep,
28    );
29
30    match result {
31        Ok(()) => unreachable!(),
32        Err(RetryError::Exhausted(last)) => {
33            println!("real-time budget exhausted after {last} attempts");
34        }
35    }
36}
More examples
Hide additional examples
examples/custom_clock.rs (line 52)
47fn main() {
48    let clock = VirtualClock::new();
49
50    // Give up once 2 seconds of *virtual* time have elapsed.
51    let policy = ExponentialBackoff::new(Duration::from_millis(100), 2.0)
52        .max_elapsed_time(&clock, Duration::from_secs(2));
53
54    let mut attempt = 0u32;
55    let outcome: Result<(), RetryError<u32>> = retry_sync(
56        policy,
57        || {
58            attempt += 1;
59            // This operation never succeeds, so we lean on the time budget.
60            ControlFlow::Continue(attempt)
61        },
62        // "Sleeping" just moves virtual time forward — no real waiting.
63        |delay| {
64            clock.advance(delay);
65            println!("slept {delay:?}, virtual t = {}ms", clock.now());
66        },
67    );
68
69    match outcome {
70        Ok(()) => unreachable!("operation never breaks"),
71        Err(RetryError::Exhausted(last)) => {
72            println!(
73                "budget exhausted after {last} attempts at virtual t = {}ms",
74                clock.now()
75            );
76        }
77    }
78}

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§