Skip to main content

tako_rs_core/queue/
retry.rs

1//! Retry policy for failed jobs.
2
3use std::time::Duration;
4
5/// Retry policy for failed jobs.
6#[derive(Debug, Clone, Default)]
7pub enum RetryPolicy {
8  /// No retries — failed jobs go straight to the dead letter queue.
9  #[default]
10  None,
11  /// Fixed delay between retries.
12  Fixed {
13    /// Maximum number of retry attempts.
14    max_retries: u32,
15    /// Delay between each retry.
16    delay: Duration,
17  },
18  /// Exponential backoff between retries.
19  Exponential {
20    /// Maximum number of retry attempts.
21    max_retries: u32,
22    /// Initial delay (doubled on each retry).
23    base_delay: Duration,
24  },
25}
26
27impl RetryPolicy {
28  /// Create a fixed-delay retry policy.
29  pub fn fixed(max_retries: u32, delay: Duration) -> Self {
30    Self::Fixed { max_retries, delay }
31  }
32
33  /// Create an exponential-backoff retry policy.
34  pub fn exponential(max_retries: u32, base_delay: Duration) -> Self {
35    Self::Exponential {
36      max_retries,
37      base_delay,
38    }
39  }
40
41  pub(crate) fn max_retries(&self) -> u32 {
42    match self {
43      Self::None => 0,
44      Self::Fixed { max_retries, .. } | Self::Exponential { max_retries, .. } => *max_retries,
45    }
46  }
47
48  pub(crate) fn delay_for_attempt(&self, attempt: u32) -> Duration {
49    match self {
50      Self::None => Duration::ZERO,
51      Self::Fixed { delay, .. } => *delay,
52      Self::Exponential { base_delay, .. } => {
53        // `Duration * u32` panics on overflow; `base_delay = 1s, attempt = 64`
54        // wraps 2^64 nanos into `u128 * u128` overflow in `Duration::Mul`.
55        // Fall back to a 1-day ceiling — any retry waiting longer than that
56        // is effectively a dead job; the queue's DLQ pathway should kick in.
57        base_delay
58          .checked_mul(2u32.saturating_pow(attempt))
59          .unwrap_or(Duration::from_secs(86_400))
60      }
61    }
62  }
63}