1use std::time::Duration;
2
3#[derive(Debug, Clone)]
8pub struct ReconnectPolicy {
9 base_delay: Duration,
10 max_delay: Duration,
11 max_retries: Option<u32>,
12 current_attempt: u32,
13}
14
15impl ReconnectPolicy {
16 pub fn new(base_delay: Duration, max_delay: Duration, max_retries: Option<u32>) -> Self {
22 Self {
23 base_delay,
24 max_delay,
25 max_retries,
26 current_attempt: 0,
27 }
28 }
29
30 pub fn default_policy() -> Self {
32 Self::new(Duration::from_secs(1), Duration::from_secs(30), None)
33 }
34
35 pub fn next_delay(&mut self) -> Option<Duration> {
39 if let Some(max) = self.max_retries {
40 if self.current_attempt >= max {
41 return None;
42 }
43 }
44
45 let multiplier = 1u32.checked_shl(self.current_attempt).unwrap_or(u32::MAX);
46 let delay = self.base_delay.saturating_mul(multiplier);
47 let delay = delay.min(self.max_delay);
48
49 self.current_attempt += 1;
50
51 Some(delay)
52 }
53
54 pub fn reset(&mut self) {
56 self.current_attempt = 0;
57 }
58
59 pub fn attempts(&self) -> u32 {
61 self.current_attempt
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn test_exponential_backoff() {
71 let mut policy =
72 ReconnectPolicy::new(Duration::from_secs(1), Duration::from_secs(30), None);
73
74 assert_eq!(policy.next_delay(), Some(Duration::from_secs(1)));
75 assert_eq!(policy.next_delay(), Some(Duration::from_secs(2)));
76 assert_eq!(policy.next_delay(), Some(Duration::from_secs(4)));
77 assert_eq!(policy.next_delay(), Some(Duration::from_secs(8)));
78 assert_eq!(policy.next_delay(), Some(Duration::from_secs(16)));
79 assert_eq!(policy.next_delay(), Some(Duration::from_secs(30)));
81 assert_eq!(policy.next_delay(), Some(Duration::from_secs(30)));
82 }
83
84 #[test]
85 fn test_reset() {
86 let mut policy =
87 ReconnectPolicy::new(Duration::from_secs(1), Duration::from_secs(30), None);
88
89 policy.next_delay();
90 policy.next_delay();
91 assert_eq!(policy.attempts(), 2);
92
93 policy.reset();
94 assert_eq!(policy.attempts(), 0);
95 assert_eq!(policy.next_delay(), Some(Duration::from_secs(1)));
96 }
97
98 #[test]
99 fn test_max_retries() {
100 let mut policy =
101 ReconnectPolicy::new(Duration::from_secs(1), Duration::from_secs(30), Some(3));
102
103 assert!(policy.next_delay().is_some());
104 assert!(policy.next_delay().is_some());
105 assert!(policy.next_delay().is_some());
106 assert!(policy.next_delay().is_none()); }
108}