pb_mapper_core/
timeout.rs1use std::time::Duration;
2
3#[derive(Debug, Clone, Copy)]
4pub struct RetryBackoff {
5 failures: u32,
6 min_delay: Duration,
7 max_delay: Duration,
8}
9
10impl Default for RetryBackoff {
11 fn default() -> Self {
12 Self::new(Duration::from_millis(100), Duration::from_secs(1))
13 }
14}
15
16impl RetryBackoff {
17 pub fn new(min_delay: Duration, max_delay: Duration) -> Self {
18 assert!(!min_delay.is_zero());
19 assert!(max_delay >= min_delay);
20 Self {
21 failures: 0,
22 min_delay,
23 max_delay,
24 }
25 }
26
27 pub fn failures(&self) -> u32 {
28 self.failures
29 }
30
31 pub fn reset(&mut self) {
32 self.failures = 0;
33 }
34
35 pub fn next_delay(&mut self) -> Duration {
36 let multiplier = 1_u32.checked_shl(self.failures.min(10)).unwrap_or(1);
37 self.failures = self.failures.saturating_add(1);
38 self.min_delay
39 .saturating_mul(multiplier)
40 .min(self.max_delay)
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn retry_backoff_caps_and_resets_without_exhausting() {
50 let mut backoff = RetryBackoff::new(Duration::from_millis(100), Duration::from_secs(1));
51
52 assert_eq!(backoff.next_delay(), Duration::from_millis(100));
53 assert_eq!(backoff.next_delay(), Duration::from_millis(200));
54 assert_eq!(backoff.next_delay(), Duration::from_millis(400));
55 assert_eq!(backoff.next_delay(), Duration::from_millis(800));
56 assert_eq!(backoff.next_delay(), Duration::from_secs(1));
57 for _ in 0..32 {
58 assert_eq!(backoff.next_delay(), Duration::from_secs(1));
59 }
60
61 assert!(backoff.failures() > 0);
62 backoff.reset();
63 assert_eq!(backoff.failures(), 0);
64 assert_eq!(backoff.next_delay(), Duration::from_millis(100));
65 }
66}