tokio_retry/strategy/
exponential_backoff.rs1use std::iter::Iterator;
2use std::time::Duration;
3use std::u64::MAX as U64_MAX;
4
5#[derive(Debug, Clone)]
9pub struct ExponentialBackoff {
10 current: u64,
11 base: u64,
12 factor: u64,
13 max_delay: Option<Duration>,
14}
15
16impl ExponentialBackoff {
17 pub fn from_millis(base: u64) -> ExponentialBackoff {
23 ExponentialBackoff {
24 current: base,
25 base: base,
26 factor: 1u64,
27 max_delay: None,
28 }
29 }
30
31 pub fn factor(mut self, factor: u64) -> ExponentialBackoff {
37 self.factor = factor;
38 self
39 }
40
41 pub fn max_delay(mut self, duration: Duration) -> ExponentialBackoff {
43 self.max_delay = Some(duration);
44 self
45 }
46}
47
48impl Iterator for ExponentialBackoff {
49 type Item = Duration;
50
51 fn next(&mut self) -> Option<Duration> {
52 let duration = if let Some(duration) = self.current.checked_mul(self.factor) {
54 Duration::from_millis(duration)
55 } else {
56 Duration::from_millis(U64_MAX)
57 };
58
59 if let Some(ref max_delay) = self.max_delay {
61 if duration > *max_delay {
62 return Some(*max_delay);
63 }
64 }
65
66 if let Some(next) = self.current.checked_mul(self.base) {
67 self.current = next;
68 } else {
69 self.current = U64_MAX;
70 }
71
72 Some(duration)
73 }
74}
75
76#[test]
77fn returns_some_exponential_base_10() {
78 let mut s = ExponentialBackoff::from_millis(10);
79
80 assert_eq!(s.next(), Some(Duration::from_millis(10)));
81 assert_eq!(s.next(), Some(Duration::from_millis(100)));
82 assert_eq!(s.next(), Some(Duration::from_millis(1000)));
83}
84
85#[test]
86fn returns_some_exponential_base_2() {
87 let mut s = ExponentialBackoff::from_millis(2);
88
89 assert_eq!(s.next(), Some(Duration::from_millis(2)));
90 assert_eq!(s.next(), Some(Duration::from_millis(4)));
91 assert_eq!(s.next(), Some(Duration::from_millis(8)));
92}
93
94#[test]
95fn saturates_at_maximum_value() {
96 let mut s = ExponentialBackoff::from_millis(U64_MAX - 1);
97
98 assert_eq!(s.next(), Some(Duration::from_millis(U64_MAX - 1)));
99 assert_eq!(s.next(), Some(Duration::from_millis(U64_MAX)));
100 assert_eq!(s.next(), Some(Duration::from_millis(U64_MAX)));
101}
102
103#[test]
104fn can_use_factor_to_get_seconds() {
105 let factor = 1000;
106 let mut s = ExponentialBackoff::from_millis(2).factor(factor);
107
108 assert_eq!(s.next(), Some(Duration::from_secs(2)));
109 assert_eq!(s.next(), Some(Duration::from_secs(4)));
110 assert_eq!(s.next(), Some(Duration::from_secs(8)));
111}
112
113#[test]
114fn stops_increasing_at_max_delay() {
115 let mut s = ExponentialBackoff::from_millis(2).max_delay(Duration::from_millis(4));
116
117 assert_eq!(s.next(), Some(Duration::from_millis(2)));
118 assert_eq!(s.next(), Some(Duration::from_millis(4)));
119 assert_eq!(s.next(), Some(Duration::from_millis(4)));
120}
121
122#[test]
123fn returns_max_when_max_less_than_base() {
124 let mut s = ExponentialBackoff::from_millis(20).max_delay(Duration::from_millis(10));
125
126 assert_eq!(s.next(), Some(Duration::from_millis(10)));
127 assert_eq!(s.next(), Some(Duration::from_millis(10)));
128}