azure_core/policies/retry_policies/
exponential_retry.rs1use super::RetryPolicy;
2use std::time::Duration;
3
4#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct ExponentialRetryPolicy {
13 initial_delay: Duration,
14 max_retries: u32,
15 max_elapsed: Duration,
16 max_delay: Duration,
17}
18
19impl ExponentialRetryPolicy {
20 pub(crate) fn new(
21 initial_delay: Duration,
22 max_retries: u32,
23 max_elapsed: Duration,
24 max_delay: Duration,
25 ) -> Self {
26 Self {
27 initial_delay: initial_delay.max(Duration::from_millis(1)),
28 max_retries,
29 max_elapsed,
30 max_delay: max_delay.max(Duration::from_secs(1)),
31 }
32 }
33}
34
35impl RetryPolicy for ExponentialRetryPolicy {
36 fn is_expired(&self, time_since_start: Duration, retry_count: u32) -> bool {
37 retry_count >= self.max_retries || time_since_start >= self.max_elapsed
38 }
39
40 fn sleep_duration(&self, retry_count: u32) -> Duration {
41 let sleep_ms = self.initial_delay.as_millis() as u64 * 2u64.pow(retry_count)
42 + u64::from(rand::random::<u8>());
43 let sleep_ms = sleep_ms.min(self.max_delay.as_millis().try_into().unwrap_or(u64::MAX));
44 Duration::from_millis(sleep_ms)
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn exponentialy_increases_correctly() {
54 let options = crate::options::ExponentialRetryOptions::default();
55 let policy = ExponentialRetryPolicy::new(
56 options.initial_delay,
57 options.max_retries,
58 options.max_total_elapsed,
59 options.max_delay,
60 );
61
62 let mut elapsed_time = Duration::from_secs(0);
63 let mut retry_count = 0;
64 let mut durations = vec![];
65 while !policy.is_expired(elapsed_time, retry_count) {
66 retry_count += 1; let duration = policy.sleep_duration(retry_count);
68 durations.push(duration);
69 elapsed_time += duration; }
71
72 let actual = durations
73 .into_iter()
74 .map(|d| d.as_secs())
75 .collect::<Vec<_>>();
76 let expected = &[0, 0, 1, 3, 6, 12, 25, 30];
77 assert_eq!(
78 actual.len(),
79 expected.len(),
80 "Different number of durations than expected"
81 );
82
83 for (&a, &e) in actual.iter().zip(expected.iter()) {
84 assert!(
86 a == e || a + 1 == e || a == e + 1,
87 "actual != expected\nActual: {actual:?}\nExpected: {expected:?}"
88 );
89 }
90 }
91}