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