futures_backoff/strategy/
mod.rs1use std::time::Duration;
2
3use super::{Action, Condition, Retry, RetryIf};
4
5mod fixed_interval;
6mod exponential_backoff;
7mod fibonacci_backoff;
8mod jitter;
9
10pub use self::fixed_interval::FixedInterval;
11pub use self::exponential_backoff::ExponentialBackoff;
12pub use self::fibonacci_backoff::FibonacciBackoff;
13pub use self::jitter::jitter;
14
15#[derive(Debug)]
16enum FactorType {
17 Exponential,
18 Fibonacci,
19 Fixed
20}
21
22#[derive(Debug)]
48pub struct Strategy {
49 factor: FactorType,
50 delay: Duration,
51 max_delay: Option<Duration>,
52 max_retries: usize,
53 jitter: bool
54}
55
56impl Default for Strategy {
57 fn default() -> Strategy {
58 Strategy {
59 factor: FactorType::Exponential,
60 delay: Duration::from_millis(1000),
61 max_delay: None,
62 max_retries: 5,
63 jitter: false
64 }
65 }
66}
67
68impl Strategy {
69 pub fn exponential(delay: Duration) -> Strategy {
74 Strategy::new(FactorType::Exponential, delay)
75 }
76
77 pub fn fibonacci(delay: Duration) -> Strategy {
89 Strategy::new(FactorType::Fibonacci, delay)
90 }
91
92 pub fn fixed(delay: Duration) -> Strategy {
94 Strategy::new(FactorType::Fixed, delay)
95 }
96
97 fn new(factor: FactorType, delay: Duration) -> Strategy {
98 Strategy {
99 factor: factor,
100 delay: delay,
101 max_delay: None,
102 max_retries: 5,
103 jitter: false
104 }
105 }
106
107 pub fn with_max_delay(mut self, duration: Duration) -> Self {
111 self.max_delay = Some(duration);
112 self
113 }
114
115 pub fn with_max_retries(mut self, retries: usize) -> Self {
119 self.max_retries = retries;
120 self
121 }
122
123 pub fn with_jitter(mut self, jitter: bool) -> Self {
128 self.jitter = jitter;
129 self
130 }
131
132 pub(crate) fn iter(&self) -> StrategyIter {
133 let factor_iter = match self.factor {
134 FactorType::Exponential =>
135 FactorIter::Exponential(ExponentialBackoff::new()),
136 FactorType::Fibonacci =>
137 FactorIter::Fibonacci(FibonacciBackoff::new()),
138 FactorType::Fixed =>
139 FactorIter::Fixed(FixedInterval::new())
140 };
141 StrategyIter {
142 factor_iter: factor_iter,
143 delay: self.delay,
144 max_delay: self.max_delay,
145 retries: self.max_retries,
146 jitter: self.jitter
147 }
148 }
149
150 pub fn retry<A: Action>(&self, action: A) -> Retry<A> {
152 Retry::new(self, action)
153 }
154
155 pub fn retry_if<A: Action, C>(&self, action: A, condition: C) -> RetryIf<A, C>
157 where C: Condition<A::Error>
158 {
159 RetryIf::new(self, action, condition)
160 }
161}
162
163enum FactorIter {
164 Exponential(ExponentialBackoff),
165 Fibonacci(FibonacciBackoff),
166 Fixed(FixedInterval),
167}
168
169impl Iterator for FactorIter {
170 type Item = u32;
171
172 fn next(&mut self) -> Option<u32> {
173 match self {
174 &mut FactorIter::Exponential(ref mut iter) => iter.next(),
175 &mut FactorIter::Fibonacci(ref mut iter) => iter.next(),
176 &mut FactorIter::Fixed(ref mut iter) => iter.next(),
177 }
178 }
179}
180
181pub(crate) struct StrategyIter {
182 factor_iter: FactorIter,
183 delay: Duration,
184 max_delay: Option<Duration>,
185 retries: usize,
186 jitter: bool
187}
188
189impl Iterator for StrategyIter {
190 type Item = Duration;
191
192 fn next(&mut self) -> Option<Duration> {
193 if self.retries > 0 {
194 if let Some(factor) = self.factor_iter.next() {
195 if let Some(mut delay) = self.delay.checked_mul(factor) {
196 if self.jitter {
197 delay = jitter(delay);
198 }
199 if let Some(max_delay) = self.max_delay {
200 delay = ::std::cmp::min(delay, max_delay);
201 }
202 self.retries -= 1;
203 return Some(delay)
204 }
205 }
206 }
207 None
208 }
209}
210
211#[test]
212fn fixed_returns_delay() {
213 let mut s = Strategy::fixed(Duration::from_millis(123)).iter();
214
215 assert_eq!(s.next(), Some(Duration::from_millis(123)));
216 assert_eq!(s.next(), Some(Duration::from_millis(123)));
217 assert_eq!(s.next(), Some(Duration::from_millis(123)));
218}
219
220#[test]
221fn fibonacci_returns_the_fibonacci_series_starting_at_10() {
222 let mut s = Strategy::fibonacci(Duration::from_millis(10)).iter();
223
224 assert_eq!(s.next(), Some(Duration::from_millis(10)));
225 assert_eq!(s.next(), Some(Duration::from_millis(10)));
226 assert_eq!(s.next(), Some(Duration::from_millis(20)));
227 assert_eq!(s.next(), Some(Duration::from_millis(30)));
228 assert_eq!(s.next(), Some(Duration::from_millis(50)));
229}
230
231#[test]
232fn fibonacci_stops_increasing_at_max_delay() {
233 let mut s = Strategy::fibonacci(Duration::from_millis(10))
234 .with_max_delay(Duration::from_millis(30)).iter();
235
236 assert_eq!(s.next(), Some(Duration::from_millis(10)));
237 assert_eq!(s.next(), Some(Duration::from_millis(10)));
238 assert_eq!(s.next(), Some(Duration::from_millis(20)));
239 assert_eq!(s.next(), Some(Duration::from_millis(30)));
240 assert_eq!(s.next(), Some(Duration::from_millis(30)));
241}
242
243#[test]
244fn fibonacci_returns_max_when_max_less_than_base() {
245 let mut s = Strategy::fibonacci(Duration::from_millis(10))
246 .with_max_delay(Duration::from_millis(10)).iter();
247
248 assert_eq!(s.next(), Some(Duration::from_millis(10)));
249 assert_eq!(s.next(), Some(Duration::from_millis(10)));
250}
251
252#[test]
253fn exponential_returns_multiples_of_10ms() {
254 let mut s = Strategy::exponential(Duration::from_millis(10)).iter();
255
256 assert_eq!(s.next(), Some(Duration::from_millis(10)));
257 assert_eq!(s.next(), Some(Duration::from_millis(20)));
258 assert_eq!(s.next(), Some(Duration::from_millis(40)));
259}
260
261#[test]
262fn exponential_returns_multiples_of_100ms() {
263 let mut s = Strategy::exponential(Duration::from_millis(100)).iter();
264
265 assert_eq!(s.next(), Some(Duration::from_millis(100)));
266 assert_eq!(s.next(), Some(Duration::from_millis(200)));
267 assert_eq!(s.next(), Some(Duration::from_millis(400)));
268}
269
270#[test]
271fn exponential_stops_increasing_at_max_delay() {
272 let mut s = Strategy::exponential(Duration::from_millis(20))
273 .with_max_delay(Duration::from_millis(40)).iter();
274
275 assert_eq!(s.next(), Some(Duration::from_millis(20)));
276 assert_eq!(s.next(), Some(Duration::from_millis(40)));
277 assert_eq!(s.next(), Some(Duration::from_millis(40)));
278}
279
280#[test]
281fn exponential_returns_max_when_max_less_than_base() {
282 let mut s = Strategy::exponential(Duration::from_millis(20))
283 .with_max_delay(Duration::from_millis(10)).iter();
284
285 assert_eq!(s.next(), Some(Duration::from_millis(10)));
286 assert_eq!(s.next(), Some(Duration::from_millis(10)));
287}