1use std::{iter::Iterator, time::Duration};
5
6use rand::Rng as _;
7
8#[derive(Debug, Clone)]
35pub struct ExponentialBackoff {
36 initial_delay: Duration,
37 next: Duration,
38 factor: f64,
39 max_delay: Duration,
40 max_jitter: Duration,
41}
42
43impl ExponentialBackoff {
44 pub fn new(initial_delay: Duration, max_delay: Duration) -> ExponentialBackoff {
46 ExponentialBackoff {
47 initial_delay,
48 next: initial_delay,
49 factor: 1.2,
50 max_delay,
51 max_jitter: initial_delay,
52 }
53 }
54
55 pub fn reset(&mut self) {
57 self.next = self.initial_delay;
58 }
59
60 pub fn factor(mut self, factor: f64) -> ExponentialBackoff {
65 self.factor = factor;
66 self
67 }
68
69 pub fn max_jitter(mut self, max_jitter: Duration) -> ExponentialBackoff {
73 self.max_jitter = max_jitter;
74 self
75 }
76}
77
78impl Iterator for ExponentialBackoff {
79 type Item = Duration;
80
81 fn next(&mut self) -> Option<Duration> {
83 let current = self.next;
84
85 let jitter = if self.max_jitter.is_zero() {
86 Duration::ZERO
87 } else {
88 Duration::from_secs_f64(
89 rand::thread_rng().gen_range(0.0..self.max_jitter.as_secs_f64()),
90 )
91 };
92 self.next = current
93 .mul_f64(self.factor)
94 .min(self.max_delay)
95 .saturating_add(jitter);
96
97 Some(current)
98 }
99}
100
101#[test]
102fn test_exponential_backoff_default() {
103 let mut backoff = ExponentialBackoff::new(Duration::from_millis(50), Duration::from_secs(10));
104
105 let bounds = vec![
106 (Duration::from_millis(50), Duration::from_millis(100)),
107 (Duration::from_millis(60), Duration::from_millis(170)),
108 ];
109 for (lower, upper) in bounds {
110 let delay = backoff.next().unwrap();
111 assert!(delay >= lower && delay <= upper);
112 }
113}
114
115#[test]
116fn test_exponential_backoff_base_100_factor_2_no_jitter() {
117 let mut backoff = ExponentialBackoff::new(Duration::from_millis(100), Duration::from_secs(10))
118 .factor(2.0)
119 .max_jitter(Duration::ZERO);
120
121 assert_eq!(backoff.next(), Some(Duration::from_millis(100)));
122 assert_eq!(backoff.next(), Some(Duration::from_millis(200)));
123 assert_eq!(backoff.next(), Some(Duration::from_millis(400)));
124 assert_eq!(backoff.next(), Some(Duration::from_millis(800)));
125}
126
127#[test]
128fn test_exponential_backoff_max_delay() {
129 let mut backoff = ExponentialBackoff::new(Duration::from_millis(200), Duration::from_secs(1))
130 .factor(3.0)
131 .max_jitter(Duration::ZERO);
132
133 assert_eq!(backoff.next(), Some(Duration::from_millis(200)));
134 assert_eq!(backoff.next(), Some(Duration::from_millis(600)));
135
136 for _ in 0..10 {
137 assert_eq!(backoff.next(), Some(Duration::from_secs(1)));
138 }
139}
140
141#[test]
142fn test_exponential_backoff_reset() {
143 let mut backoff = ExponentialBackoff::new(Duration::from_millis(100), Duration::from_secs(10))
144 .factor(2.0)
145 .max_jitter(Duration::ZERO);
146
147 assert_eq!(backoff.next(), Some(Duration::from_millis(100)));
148 assert_eq!(backoff.next(), Some(Duration::from_millis(200)));
149 assert_eq!(backoff.next(), Some(Duration::from_millis(400)));
150
151 backoff.reset();
152
153 assert_eq!(backoff.next(), Some(Duration::from_millis(100)));
154 assert_eq!(backoff.next(), Some(Duration::from_millis(200)));
155}