retry_future/retry_strategy/
exponential.rs1use crate::{RetryStrategy, TooManyAttempts};
2use std::time::Duration;
3
4#[derive(Debug, Copy, Clone)]
29pub struct ExponentialRetryStrategy {
30 pub base: usize,
31 pub max_attempts: usize,
32 pub initial_delay: Duration,
33 pub retry_early_returned_errors: bool,
35}
36
37impl Default for ExponentialRetryStrategy {
38 fn default() -> Self {
39 Self {
40 base: 2,
41 max_attempts: 3,
42 initial_delay: Duration::from_millis(500),
43 retry_early_returned_errors: true,
44 }
45 }
46}
47
48impl ExponentialRetryStrategy {
49 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn max_attempts(mut self, max_attempts: usize) -> Self {
54 self.max_attempts = max_attempts;
55 self
56 }
57
58 pub fn initial_delay(mut self, initial_delay: Duration) -> Self {
59 self.initial_delay = initial_delay;
60 self
61 }
62
63 pub fn retry_early_returned_errors(mut self, retry_early_returned_errors: bool) -> Self {
65 self.retry_early_returned_errors = retry_early_returned_errors;
66 self
67 }
68}
69
70impl RetryStrategy for ExponentialRetryStrategy {
71 fn check_attempt(&mut self, attempts_before: usize) -> Result<Duration, TooManyAttempts> {
72 let exponent = self.base.pow(attempts_before as u32);
73 if self.max_attempts == attempts_before {
74 Err(TooManyAttempts)
75 } else {
76 Ok(self.initial_delay * exponent as u32)
77 }
78 }
79
80 fn retry_early_returned_errors(&self) -> bool {
81 self.retry_early_returned_errors
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn check_exponent() {
91 let mut strategy = ExponentialRetryStrategy {
92 base: 2,
93 initial_delay: Duration::from_secs(1),
94 max_attempts: 5,
95 ..Default::default()
96 };
97 assert_eq!(strategy.check_attempt(0).unwrap(), Duration::from_secs(1));
98 assert_eq!(strategy.check_attempt(1).unwrap(), Duration::from_secs(2));
99 assert_eq!(strategy.check_attempt(2).unwrap(), Duration::from_secs(4));
100 assert_eq!(strategy.check_attempt(3).unwrap(), Duration::from_secs(8));
101 assert_eq!(strategy.check_attempt(4).unwrap(), Duration::from_secs(16));
102
103 assert!(strategy.check_attempt(5).is_err());
104 }
105}