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§
Sourcefn max_attempts(self, max: u32) -> MaxAttempts<Self>
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
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}Sourcefn with_max_delay(self, max_delay: Duration) -> WithMaxDelay<Self>
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}Sourcefn max_elapsed_time<C: Clock>(
self,
clock: C,
max_elapsed: Duration,
) -> MaxElapsedTime<Self, C>
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
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".