1use instant::Instant;
2use std::marker::PhantomData;
3use std::time::Duration;
4
5use crate::backoff::Backoff;
6use crate::clock::Clock;
7use crate::default;
8
9#[derive(Debug)]
10pub struct ExponentialBackoff<C> {
11 pub current_interval: Duration,
13 pub initial_interval: Duration,
15 pub randomization_factor: f64,
20 pub multiplier: f64,
22 pub max_interval: Duration,
25 pub start_time: Instant,
28 pub max_elapsed_time: Option<Duration>,
31 pub clock: C,
33}
34
35impl<C> Default for ExponentialBackoff<C>
36where
37 C: Clock + Default,
38{
39 fn default() -> ExponentialBackoff<C> {
40 let mut eb = ExponentialBackoff {
41 current_interval: Duration::from_millis(default::INITIAL_INTERVAL_MILLIS),
42 initial_interval: Duration::from_millis(default::INITIAL_INTERVAL_MILLIS),
43 randomization_factor: default::RANDOMIZATION_FACTOR,
44 multiplier: default::MULTIPLIER,
45 max_interval: Duration::from_millis(default::MAX_INTERVAL_MILLIS),
46 max_elapsed_time: Some(Duration::from_millis(default::MAX_ELAPSED_TIME_MILLIS)),
47 clock: C::default(),
48 start_time: Instant::now(),
49 };
50 eb.reset();
51 eb
52 }
53}
54
55impl<C: Clock> ExponentialBackoff<C> {
56 pub fn get_elapsed_time(&self) -> Duration {
58 self.clock.now().duration_since(self.start_time)
59 }
60
61 fn get_random_value_from_interval(
62 randomization_factor: f64,
63 random: f64,
64 current_interval: Duration,
65 ) -> Duration {
66 let current_interval_nanos = duration_to_nanos(current_interval);
67
68 let delta = randomization_factor * current_interval_nanos;
69 let min_interval = current_interval_nanos - delta;
70 let max_interval = current_interval_nanos + delta;
71 let diff = max_interval - min_interval;
75 let nanos = min_interval + (random * (diff + 1.0));
76 nanos_to_duration(nanos)
77 }
78
79 fn increment_current_interval(&mut self) -> Duration {
80 let current_interval_nanos = duration_to_nanos(self.current_interval);
81 let max_interval_nanos = duration_to_nanos(self.max_interval);
82 if current_interval_nanos >= max_interval_nanos / self.multiplier {
84 self.max_interval
85 } else {
86 let nanos = current_interval_nanos * self.multiplier;
87 nanos_to_duration(nanos)
88 }
89 }
90}
91
92fn duration_to_nanos(d: Duration) -> f64 {
93 d.as_secs() as f64 * 1_000_000_000.0 + f64::from(d.subsec_nanos())
94}
95
96fn nanos_to_duration(nanos: f64) -> Duration {
97 let secs = nanos / 1_000_000_000.0;
98 let nanos = nanos as u64 % 1_000_000_000;
99 Duration::new(secs as u64, nanos as u32)
100}
101
102impl<C> Backoff for ExponentialBackoff<C>
103where
104 C: Clock,
105{
106 fn reset(&mut self) {
107 self.current_interval = self.initial_interval;
108 self.start_time = self.clock.now();
109 }
110
111 fn next_backoff(&mut self) -> Option<Duration> {
112 let elapsed_time = self.get_elapsed_time();
113
114 match self.max_elapsed_time {
115 Some(v) if elapsed_time > v => None,
116 _ => {
117 let random = rand::random::<f64>();
118 let randomized_interval = Self::get_random_value_from_interval(
119 self.randomization_factor,
120 random,
121 self.current_interval,
122 );
123 self.current_interval = self.increment_current_interval();
124
125 if let Some(max_elapsed_time) = self.max_elapsed_time {
126 if elapsed_time + randomized_interval <= max_elapsed_time {
127 Some(randomized_interval)
128 } else {
129 None
130 }
131 } else {
132 Some(randomized_interval)
133 }
134 }
135 }
136 }
137}
138
139impl<C> Clone for ExponentialBackoff<C>
140where
141 C: Clone,
142{
143 fn clone(&self) -> Self {
144 let clock = self.clock.clone();
145 ExponentialBackoff { clock, ..*self }
146 }
147}
148
149#[derive(Debug)]
153pub struct ExponentialBackoffBuilder<C> {
154 initial_interval: Duration,
155 randomization_factor: f64,
156 multiplier: f64,
157 max_interval: Duration,
158 max_elapsed_time: Option<Duration>,
159 _clock: PhantomData<C>,
160}
161
162impl<C> Default for ExponentialBackoffBuilder<C> {
163 fn default() -> Self {
164 Self {
165 initial_interval: Duration::from_millis(default::INITIAL_INTERVAL_MILLIS),
166 randomization_factor: default::RANDOMIZATION_FACTOR,
167 multiplier: default::MULTIPLIER,
168 max_interval: Duration::from_millis(default::MAX_INTERVAL_MILLIS),
169 max_elapsed_time: Some(Duration::from_millis(default::MAX_ELAPSED_TIME_MILLIS)),
170 _clock: PhantomData,
171 }
172 }
173}
174
175impl<C> ExponentialBackoffBuilder<C>
176where
177 C: Clock + Default,
178{
179 pub fn new() -> Self {
180 Default::default()
181 }
182
183 pub fn with_initial_interval(&mut self, initial_interval: Duration) -> &mut Self {
185 self.initial_interval = initial_interval;
186 self
187 }
188
189 pub fn with_randomization_factor(&mut self, randomization_factor: f64) -> &mut Self {
194 self.randomization_factor = randomization_factor;
195 self
196 }
197
198 pub fn with_multiplier(&mut self, multiplier: f64) -> &mut Self {
200 self.multiplier = multiplier;
201 self
202 }
203
204 pub fn with_max_interval(&mut self, max_interval: Duration) -> &mut Self {
207 self.max_interval = max_interval;
208 self
209 }
210
211 pub fn with_max_elapsed_time(&mut self, max_elapsed_time: Option<Duration>) -> &mut Self {
214 self.max_elapsed_time = max_elapsed_time;
215 self
216 }
217
218 pub fn build(&self) -> ExponentialBackoff<C> {
219 ExponentialBackoff {
220 current_interval: self.initial_interval,
221 initial_interval: self.initial_interval,
222 randomization_factor: self.randomization_factor,
223 multiplier: self.multiplier,
224 max_interval: self.max_interval,
225 max_elapsed_time: self.max_elapsed_time,
226 clock: C::default(),
227 start_time: Instant::now(),
228 }
229 }
230}
231
232#[cfg(test)]
233use crate::clock::SystemClock;
234
235#[test]
236fn get_randomized_interval() {
237 let f = ExponentialBackoff::<SystemClock>::get_random_value_from_interval;
239 assert_eq!(Duration::new(0, 1), f(0.5, 0.0, Duration::new(0, 2)));
240 assert_eq!(Duration::new(0, 1), f(0.5, 0.33, Duration::new(0, 2)));
241 assert_eq!(Duration::new(0, 2), f(0.5, 0.34, Duration::new(0, 2)));
243 assert_eq!(Duration::new(0, 2), f(0.5, 0.66, Duration::new(0, 2)));
244 assert_eq!(Duration::new(0, 3), f(0.5, 0.67, Duration::new(0, 2)));
246 assert_eq!(Duration::new(0, 3), f(0.5, 0.99, Duration::new(0, 2)));
247}
248
249#[test]
250fn exponential_backoff_builder() {
251 let initial_interval = Duration::from_secs(1);
252 let max_interval = Duration::from_secs(2);
253 let multiplier = 3.0;
254 let randomization_factor = 4.0;
255 let backoff: ExponentialBackoff<SystemClock> = ExponentialBackoffBuilder::new()
256 .with_initial_interval(initial_interval)
257 .with_multiplier(multiplier)
258 .with_randomization_factor(randomization_factor)
259 .with_max_interval(max_interval)
260 .with_max_elapsed_time(None)
261 .build();
262 assert_eq!(backoff.initial_interval, initial_interval);
263 assert_eq!(backoff.current_interval, initial_interval);
264 assert_eq!(backoff.multiplier, multiplier);
265 assert_eq!(backoff.randomization_factor, randomization_factor);
266 assert_eq!(backoff.max_interval, max_interval);
267 assert_eq!(backoff.max_elapsed_time, None);
268}
269
270#[test]
271fn exponential_backoff_default_builder() {
272 let backoff: ExponentialBackoff<SystemClock> = ExponentialBackoffBuilder::new().build();
273 assert_eq!(
274 backoff.initial_interval,
275 Duration::from_millis(default::INITIAL_INTERVAL_MILLIS)
276 );
277 assert_eq!(
278 backoff.current_interval,
279 Duration::from_millis(default::INITIAL_INTERVAL_MILLIS)
280 );
281 assert_eq!(backoff.multiplier, default::MULTIPLIER);
282 assert_eq!(backoff.randomization_factor, default::RANDOMIZATION_FACTOR);
283 assert_eq!(
284 backoff.max_interval,
285 Duration::from_millis(default::MAX_INTERVAL_MILLIS)
286 );
287 assert_eq!(
288 backoff.max_elapsed_time,
289 Some(Duration::from_millis(default::MAX_ELAPSED_TIME_MILLIS))
290 );
291}