pub struct Backoff { /* private fields */ }Expand description
Re-exported from reliakit-backoff so the backoff schedule is reachable
without a separate dependency line.
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
impl Backoff
Sourcepub const fn constant(base: Duration) -> Backoff
pub const fn constant(base: Duration) -> Backoff
A constant backoff that always waits base.
Examples found in repository?
29fn main() {
30 let policy = RetryPolicy::new(4, Backoff::constant(Duration::from_millis(20)))
31 .expect("max_attempts is non-zero");
32
33 let mut attempt = 0;
34 let result: Result<u32, RetryError<&str>> = block_on(retry_async(
35 &policy,
36 || {
37 attempt += 1;
38 let outcome = if attempt < 3 {
39 Err("temporary")
40 } else {
41 Ok(200)
42 };
43 async move { outcome }
44 },
45 |_error| true,
46 |delay| async move {
47 // Your runtime's async sleep goes here. This example resolves
48 // immediately; the sleep future is entirely user-provided.
49 let _ = delay;
50 },
51 ));
52
53 println!("async result after {attempt} attempt(s): {result:?}");
54}Sourcepub const fn linear(base: Duration, step: Duration) -> Backoff
pub const fn linear(base: Duration, step: Duration) -> Backoff
A linear backoff: attempt n waits base + step * n.
Sourcepub const fn exponential(base: Duration, factor: u32) -> Backoff
pub const fn exponential(base: Duration, factor: u32) -> Backoff
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?
17fn main() {
18 let policy = RetryPolicy::new(
19 5,
20 Backoff::exponential(Duration::from_millis(50), 2).with_max_delay(Duration::from_secs(1)),
21 )
22 .expect("max_attempts is non-zero");
23
24 // An operation that fails twice with a temporary error, then succeeds.
25 let mut attempt = 0;
26 let result: Result<&str, RetryError<ApiError>> = retry_with_sleep(
27 &policy,
28 || {
29 attempt += 1;
30 println!("attempt {attempt}");
31 if attempt < 3 {
32 Err(ApiError::Temporary)
33 } else {
34 Ok("payload")
35 }
36 },
37 |error| matches!(error, ApiError::Temporary), // retry only temporary errors
38 |delay| {
39 // You provide the waiting. In real code, call your platform or
40 // runtime sleep here; this example only reports the delay so it
41 // stays dependency-free and instant.
42 println!(" would wait {delay:?} before the next attempt");
43 },
44 );
45 match result {
46 Ok(body) => println!("succeeded with: {body}"),
47 Err(error) => println!("gave up: {error:?}"),
48 }
49
50 // A fatal error stops immediately, even though attempts remain.
51 let mut attempt = 0;
52 let result: Result<&str, RetryError<ApiError>> = retry_with_sleep(
53 &policy,
54 || {
55 attempt += 1;
56 Err(ApiError::Fatal)
57 },
58 |error| matches!(error, ApiError::Temporary),
59 |_delay| {},
60 );
61 println!(
62 "fatal path: stopped after {} attempt(s)",
63 result.unwrap_err().attempts()
64 );
65}Sourcepub const fn with_max_delay(self, max_delay: Duration) -> Backoff
pub const fn with_max_delay(self, max_delay: Duration) -> Backoff
Caps every computed delay at max_delay.
Examples found in repository?
17fn main() {
18 let policy = RetryPolicy::new(
19 5,
20 Backoff::exponential(Duration::from_millis(50), 2).with_max_delay(Duration::from_secs(1)),
21 )
22 .expect("max_attempts is non-zero");
23
24 // An operation that fails twice with a temporary error, then succeeds.
25 let mut attempt = 0;
26 let result: Result<&str, RetryError<ApiError>> = retry_with_sleep(
27 &policy,
28 || {
29 attempt += 1;
30 println!("attempt {attempt}");
31 if attempt < 3 {
32 Err(ApiError::Temporary)
33 } else {
34 Ok("payload")
35 }
36 },
37 |error| matches!(error, ApiError::Temporary), // retry only temporary errors
38 |delay| {
39 // You provide the waiting. In real code, call your platform or
40 // runtime sleep here; this example only reports the delay so it
41 // stays dependency-free and instant.
42 println!(" would wait {delay:?} before the next attempt");
43 },
44 );
45 match result {
46 Ok(body) => println!("succeeded with: {body}"),
47 Err(error) => println!("gave up: {error:?}"),
48 }
49
50 // A fatal error stops immediately, even though attempts remain.
51 let mut attempt = 0;
52 let result: Result<&str, RetryError<ApiError>> = retry_with_sleep(
53 &policy,
54 || {
55 attempt += 1;
56 Err(ApiError::Fatal)
57 },
58 |error| matches!(error, ApiError::Temporary),
59 |_delay| {},
60 );
61 println!(
62 "fatal path: stopped after {} attempt(s)",
63 result.unwrap_err().attempts()
64 );
65}Sourcepub const fn with_max_retries(self, max_retries: u32) -> Backoff
pub const fn with_max_retries(self, max_retries: u32) -> Backoff
Limits the policy to at most max_retries retries.
After this, delay returns None for attempt numbers
>= max_retries.
Sourcepub const fn max_retries(&self) -> Option<u32>
pub const fn max_retries(&self) -> Option<u32>
Returns the retry limit, if any.
Sourcepub fn delay(&self, attempt: u32) -> Option<Duration>
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.