1use std::time::Duration;
2
3#[derive(Debug, Clone)]
4pub struct ExponentialBackoff {
6 max_sleep: Duration,
7 max_retries: Option<u32>,
8 current_sleep: Duration,
9 retry_count: u32,
10}
11
12impl ExponentialBackoff {
13 pub fn new(max_sleep: Duration, max_retries: Option<u32>, initial_sleep: Duration) -> Self {
15 Self {
16 max_sleep,
17 max_retries,
18 current_sleep: initial_sleep,
19 retry_count: 0,
20 }
21 }
22}
23
24impl Iterator for ExponentialBackoff {
25 type Item = Duration;
26
27 fn next(&mut self) -> Option<Self::Item> {
28 if self.max_retries.is_some_and(|max| max <= self.retry_count) {
29 return None;
30 }
31
32 let next_sleep = self.current_sleep;
33 self.current_sleep = self.max_sleep.min(self.current_sleep * 2);
34 self.retry_count += 1;
35
36 Some(next_sleep)
37 }
38}
39
40#[derive(Debug, Clone)]
41pub struct SessionRetryPolicy {
46 reconnect_max_sleep: Duration,
47 reconnect_retry_limit: Option<u32>,
48 reconnect_initial_sleep: Duration,
49}
50
51impl Default for SessionRetryPolicy {
52 fn default() -> Self {
53 Self {
54 reconnect_max_sleep: Duration::from_millis(Self::DEFAULT_MAX_SLEEP_MS),
55 reconnect_retry_limit: Some(Self::DEFAULT_RETRY_LIMIT),
56 reconnect_initial_sleep: Duration::from_millis(Self::DEFAULT_INITIAL_SLEEP_MS),
57 }
58 }
59}
60
61impl SessionRetryPolicy {
62 pub const DEFAULT_RETRY_LIMIT: u32 = 10;
64 pub const DEFAULT_INITIAL_SLEEP_MS: u64 = 500;
66 pub const DEFAULT_MAX_SLEEP_MS: u64 = 30000;
68
69 pub fn new(max_sleep: Duration, retry_limit: Option<u32>, initial_sleep: Duration) -> Self {
71 Self {
72 reconnect_max_sleep: max_sleep,
73 reconnect_retry_limit: retry_limit,
74 reconnect_initial_sleep: initial_sleep,
75 }
76 }
77
78 pub(crate) fn new_backoff(&self) -> ExponentialBackoff {
79 ExponentialBackoff::new(
80 self.reconnect_max_sleep,
81 self.reconnect_retry_limit,
82 self.reconnect_initial_sleep,
83 )
84 }
85
86 pub fn infinity(max_sleep: Duration, initial_sleep: Duration) -> Self {
88 Self {
89 reconnect_initial_sleep: initial_sleep,
90 reconnect_retry_limit: None,
91 reconnect_max_sleep: max_sleep,
92 }
93 }
94
95 pub fn never() -> Self {
97 Self {
98 reconnect_retry_limit: Some(0),
99 ..Default::default()
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use std::time::Duration;
107
108 use super::SessionRetryPolicy;
109
110 #[test]
111 fn session_retry() {
112 let policy = SessionRetryPolicy::default();
113
114 let mut backoff = policy.new_backoff();
115
116 assert_eq!(Some(Duration::from_millis(500)), backoff.next());
117 assert_eq!(Some(Duration::from_millis(1000)), backoff.next());
118 assert_eq!(Some(Duration::from_millis(2000)), backoff.next());
119 assert_eq!(Some(Duration::from_millis(4000)), backoff.next());
120 assert_eq!(Some(Duration::from_millis(8000)), backoff.next());
121 assert_eq!(Some(Duration::from_millis(16000)), backoff.next());
122 assert_eq!(Some(Duration::from_millis(30000)), backoff.next());
123 assert_eq!(Some(Duration::from_millis(30000)), backoff.next());
124 assert_eq!(Some(Duration::from_millis(30000)), backoff.next());
125 assert_eq!(Some(Duration::from_millis(30000)), backoff.next());
126 assert_eq!(None, backoff.next());
127 assert_eq!(None, backoff.next());
128 }
129
130 #[test]
131 fn session_retry_infinity() {
132 let policy =
133 SessionRetryPolicy::infinity(Duration::from_millis(3000), Duration::from_millis(500));
134
135 let mut backoff = policy.new_backoff();
136
137 for _ in 0..100 {
138 assert!(backoff.next().is_some());
139 }
140
141 assert_eq!(Some(Duration::from_millis(3000)), backoff.next());
142 }
143
144 #[test]
145 fn session_retry_never() {
146 let policy = SessionRetryPolicy::never();
147 let mut backoff = policy.new_backoff();
148 assert!(backoff.next().is_none());
149 }
150}