pub struct Backoff { /* private fields */ }Expand description
The retry state of one loop.
A loop holds one of these, tells it when an attempt succeeded or failed, and asks it to wait. Nothing else in the loop needs to know about multiplying, ceilings or slicing.
§Examples
use std::time::Duration;
use graceful_worker::Backoff;
let mut backoff = Backoff::new();
assert_eq!(backoff.delay(), Duration::from_secs(5));
backoff.failed();
assert_eq!(backoff.delay(), Duration::from_secs(10));
backoff.failed();
assert_eq!(backoff.delay(), Duration::from_secs(20));
// Any success puts it straight back to the start.
backoff.succeeded();
assert_eq!(backoff.delay(), Duration::from_secs(5));
assert_eq!(backoff.attempt(), 0);A schedule of your own:
use std::time::Duration;
use graceful_worker::Backoff;
let backoff = Backoff::new()
.with_initial_delay(Duration::from_millis(100))
.with_max_delay(Duration::from_secs(30))
.with_slice(Duration::from_secs(5))
.with_factor(3);
assert_eq!(backoff.delay(), Duration::from_millis(100));Implementations§
Source§impl Backoff
impl Backoff
Sourcepub const fn with_initial_delay(self, initial: Duration) -> Self
pub const fn with_initial_delay(self, initial: Duration) -> Self
Set the first delay, and reset to it.
Clamped to the ceiling, so an initial delay longer than the maximum is the maximum rather than a schedule that counts downwards.
Sourcepub const fn with_max_delay(self, max: Duration) -> Self
pub const fn with_max_delay(self, max: Duration) -> Self
Set the ceiling.
Sourcepub const fn with_slice(self, slice: Duration) -> Self
pub const fn with_slice(self, slice: Duration) -> Self
Set the longest single sleep Backoff::wait performs.
This is the worst case for how long a stop goes unnoticed during a
wait, so it should be comfortably shorter than the platform’s grace
period. Duration::ZERO disables slicing, which restores the
behaviour of every other backoff crate — including its drawback.
Sourcepub const fn with_factor(self, factor: u32) -> Self
pub const fn with_factor(self, factor: u32) -> Self
Set the multiplier applied after each failure.
A factor of 1 is a constant delay. Zero is treated as 1, because a backoff that multiplies by zero would retry instantly forever.
Sourcepub const fn attempt(&self) -> u32
pub const fn attempt(&self) -> u32
How many consecutive failures there have been.
Useful as a dimension on a retry metric.
Sourcepub const fn is_retrying(&self) -> bool
pub const fn is_retrying(&self) -> bool
Whether anything has failed since the last success.
A loop uses this to decide whether an empty queue is unremarkable or worth a log line.
Sourcepub const fn failed(&mut self) -> Duration
pub const fn failed(&mut self) -> Duration
Record a failure: multiply the delay, up to the ceiling.
Returns the delay that was in force for this failure, which is what a metric should record — not the multiplied value the next one will use.
Sourcepub const fn succeeded(&mut self)
pub const fn succeeded(&mut self)
Record a success: back to the initial delay, attempt count zero.
Any success resets it, not just a run of them.
Sourcepub const fn sleep_slice(&self, remaining: Duration) -> Duration
pub const fn sleep_slice(&self, remaining: Duration) -> Duration
The slice to sleep for, given how much of a delay is left.
Never zero while remaining is non-zero, which is what stops
Backoff::wait spinning.
§Examples
use std::time::Duration;
use graceful_worker::Backoff;
let backoff = Backoff::new();
// A short remainder is slept in one go.
assert_eq!(backoff.sleep_slice(Duration::from_secs(3)), Duration::from_secs(3));
// A long one is sliced, so shutdown stays responsive.
assert_eq!(backoff.sleep_slice(Duration::from_secs(900)), Duration::from_secs(60));Sourcepub async fn wait(&self, watcher: &Watcher) -> bool
pub async fn wait(&self, watcher: &Watcher) -> bool
Wait out the current delay, in slices, unless a stop arrives first.
Returns true if the whole delay elapsed, false if it was cut
short — in which case the loop should stop rather than retry.
This is the method the crate exists for. See the module documentation.
§Examples
The usual loop shape. Marked no_run because running it would do
exactly what it says: wait five real seconds.
use graceful_worker::{Backoff, Shutdown};
let shutdown = Shutdown::new();
let watcher = shutdown.watcher();
let mut backoff = Backoff::new();
while watcher.is_running() {
let worked = false; // ... do a unit of work ...
if worked {
backoff.succeeded();
} else {
backoff.failed();
if !backoff.wait(&watcher).await {
break; // asked to stop mid-wait
}
}
}