atomr_core/pattern/
backoff.rs1use std::time::Duration;
4
5#[derive(Debug, Clone)]
6pub struct BackoffOptions {
7 pub min_backoff: Duration,
8 pub max_backoff: Duration,
9 pub random_factor: f64,
10 pub max_restarts: Option<u32>,
11}
12
13impl Default for BackoffOptions {
14 fn default() -> Self {
15 Self {
16 min_backoff: Duration::from_millis(200),
17 max_backoff: Duration::from_secs(30),
18 random_factor: 0.2,
19 max_restarts: Some(10),
20 }
21 }
22}
23
24impl BackoffOptions {
25 pub fn next_delay(&self, attempt: u32) -> Duration {
26 let base = self.min_backoff.as_secs_f64() * 2f64.powi(attempt as i32);
27 let capped = base.min(self.max_backoff.as_secs_f64());
28 let jitter = 1.0 + (pseudo_random_01(attempt) * self.random_factor);
29 Duration::from_secs_f64(capped * jitter)
30 }
31}
32
33fn pseudo_random_01(seed: u32) -> f64 {
34 ((seed.wrapping_mul(2654435761)) % 10_000) as f64 / 10_000.0
37}
38
39pub struct BackoffSupervisor {
41 pub options: BackoffOptions,
42}
43
44impl BackoffSupervisor {
45 pub fn new(options: BackoffOptions) -> Self {
46 Self { options }
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn backoff_grows_but_capped() {
56 let o = BackoffOptions::default();
57 let d0 = o.next_delay(0);
58 let d1 = o.next_delay(1);
59 let huge = o.next_delay(30);
60 assert!(d0 < d1);
61 assert!(huge <= o.max_backoff.mul_f64(1.0 + o.random_factor));
62 }
63}