1use std::{cmp::min, time::Duration};
2
3pub fn get_backoff_iter() -> Backoff {
17 Backoff::default()
18}
19
20pub fn iter_with_initial_wait_ms(initial_wait_ms: u64) -> Backoff {
23 debug_assert!(initial_wait_ms <= Backoff::DEFAULT_MAX_WAIT_MS);
26
27 Backoff {
28 initial_wait_ms,
29 ..Backoff::default()
30 }
31}
32
33pub struct Backoff {
39 pub initial_wait_ms: u64,
40 pub max_wait_ms: u64,
41 pub attempt: u32,
42}
43
44impl Backoff {
45 pub const DEFAULT: Self = Self {
47 initial_wait_ms: Self::DEFAULT_INITIAL_WAIT_MS,
48 max_wait_ms: Self::DEFAULT_MAX_WAIT_MS,
49 attempt: 0,
50 };
51 pub const DEFAULT_INITIAL_WAIT_MS: u64 = 250;
52 pub const DEFAULT_MAX_WAIT_MS: u64 = 32_000;
53 const EXP_BASE: u64 = 2;
54
55 pub fn new(initial_wait_ms: u64, max_wait_ms: u64) -> Self {
57 debug_assert!(initial_wait_ms <= max_wait_ms);
58 Self {
59 initial_wait_ms,
60 max_wait_ms,
61 attempt: 0,
62 }
63 }
64
65 pub fn reset(&mut self) {
67 self.attempt = 0;
68 }
69
70 pub fn next_delay(&mut self) -> Duration {
72 let factor = Self::EXP_BASE.saturating_pow(self.attempt);
73 let wait_ms = self.initial_wait_ms.saturating_mul(factor);
74 let bounded_wait_ms = min(wait_ms, self.max_wait_ms);
75 self.attempt = self.attempt.saturating_add(1);
76 Duration::from_millis(bounded_wait_ms)
77 }
78}
79
80impl Default for Backoff {
81 fn default() -> Self {
82 Self::DEFAULT
83 }
84}
85
86impl Iterator for Backoff {
87 type Item = Duration;
88
89 #[inline]
90 fn next(&mut self) -> Option<Self::Item> {
91 Some(self.next_delay())
92 }
93}
94
95#[cfg(test)]
96mod test {
97 use super::*;
98
99 #[test]
100 fn no_integer_overflow() {
101 let mut backoff_durations = get_backoff_iter();
102 for _ in 0..200 {
103 backoff_durations.next();
104 }
105 }
106}