RetryPolicy and Backoff
Most code configures retries in the attribute. These are the types that
attribute expands into, and what tests and hand-built QueueConfigs use
directly.
RetryPolicy
Section titled “RetryPolicy”pub struct RetryPolicy { pub max_attempts: u32, pub backoff: Backoff,}
impl RetryPolicy { pub fn new(max_attempts: u32, backoff: Backoff) -> Self; pub fn none() -> Self; pub fn exponential(max_attempts: u32) -> Self; pub fn fixed(max_attempts: u32, delay: Duration) -> Self; pub fn decide(&self, failed_attempt: u32) -> RetryDecision;}
impl Default for RetryPolicy; // max_attempts: 1, Backoff::Nonemax_attempts counts the first try. 1 means no retries.
RetryPolicy::none() // 1 attempt, Backoff::NoneRetryPolicy::exponential(5) // 5 attempts, Backoff::exponential()RetryPolicy::fixed(3, Duration::from_millis(500)) // 3 attempts, constant 500msBackoff
Section titled “Backoff”pub enum Backoff { None, Fixed(Duration), Exponential { base: Duration, factor: f64, max: Duration, jitter: bool },}
impl Backoff { pub fn exponential() -> Self; // base 1s, factor 2.0, max 300s, jitter true pub fn delay_for(&self, failed_attempt: u32) -> Duration;}Backoff::None retries immediately, with a delay of Duration::ZERO.
Exponential waits min(max, base * factor^(failed_attempt - 1)). With
jitter on, the wait is drawn uniformly from [0, computed] — full jitter.
RetryDecision
Section titled “RetryDecision”pub enum RetryDecision { Retry { delay: Duration }, GiveUp,}decide(failed_attempt) returns GiveUp when
failed_attempt >= max_attempts, and otherwise Retry with
backoff.delay_for(failed_attempt).
RetryDecision is exported from the crate root but not from the prelude, since
application code rarely names it.
Defaults, side by side
Section titled “Defaults, side by side”| Source | max_attempts |
Backoff |
|---|---|---|
RetryPolicy::default() and ::none() |
1 |
Backoff::None |
QueueConfig::new(...) |
1 |
Backoff::None |
Backoff::exponential() |
— | base 1s, factor 2.0, max 300s, jitter true |
RetryPolicy::exponential(n) |
n |
Backoff::exponential() |
RetryPolicy::fixed(n, d) |
n |
Backoff::Fixed(d) |
The retry(...) attribute |
3 |
exponential, base 1s, factor 2.0, max 5m, jitter true |
The first two rows are the trap: a queue with no retry(...) does not retry
at all.
Precedence at runtime
Section titled “Precedence at runtime”Job::retry_policy(), when it returnsSome.- Otherwise
Job::QUEUE.config().retry.
delay_for never panics
Section titled “delay_for never panics”Written to be total, on purpose — a backoff calculation is the last place you want a panic:
failed_attemptis clamped to at least 1, so attempt 0 behaves like 1;- the exponentiation happens in
f64and saturates tomaxon overflow; - a non-finite or non-positive
factordegenerates to a constantbase; - a non-finite or NaN intermediate result falls back to
max; - a zero
baseor a zeromaxyieldsDuration::ZERO; - the result is always less than or equal to
max.
Both types are serializable
Section titled “Both types are serializable”RetryPolicy and Backoff are Serialize + Deserialize + Clone + PartialEq + Debug, so a policy can come from configuration rather than from an attribute
when a deployment needs to tune it without a rebuild.
See also
Section titled “See also”Retries and backoff, and
queuey_core::retry
on docs.rs.