1use rand::Rng;
24use std::time::Duration;
25
26pub struct ExponentialBackoffBuilder {
28 factor: f64,
29 interval: Duration,
30 jitter: Duration,
31 max: Option<Duration>,
32}
33
34impl Default for ExponentialBackoffBuilder {
35 #[inline]
36 fn default() -> Self {
37 Self {
38 factor: 1.75,
39 interval: Duration::from_millis(500),
40 jitter: Duration::from_millis(150),
41 max: None,
42 }
43 }
44}
45
46impl ExponentialBackoffBuilder {
47 #[inline]
49 pub const fn factor(mut self, factor: f64) -> Self {
50 self.factor = factor;
51 self
52 }
53
54 pub const fn interval(mut self, interval: Duration) -> Self {
56 self.interval = interval;
57 self
58 }
59
60 #[inline]
62 pub const fn jitter(mut self, jitter: Duration) -> Self {
63 self.jitter = jitter;
64 self
65 }
66
67 #[inline]
69 pub const fn max(mut self, max: Duration) -> Self {
70 self.max = Some(max);
71 self
72 }
73
74 #[inline]
76 pub const fn build(self) -> Exponential {
77 Exponential {
78 factor: self.factor,
79 interval: self.interval.as_nanos() as f64,
80 jitter: self.jitter.as_nanos() as f64,
81 max: match self.max {
82 Some(d) => Some(d.as_nanos() as u64),
83 None => None,
84 },
85 }
86 }
87}
88
89pub struct Exponential {
91 factor: f64,
92 interval: f64,
93 jitter: f64,
94 max: Option<u64>,
95}
96
97impl Exponential {
98 pub fn duration(&self, attempt: usize) -> Duration {
100 let nanoseconds = (self.factor.powi(attempt as i32) * self.interval
101 + rand::thread_rng().gen_range(0.0..=self.jitter)) as u64;
102 match self.max {
103 Some(max) if nanoseconds > max => Duration::from_nanos(max),
104 _ => Duration::from_nanos(nanoseconds),
105 }
106 }
107}
108
109#[cfg(test)]
110mod tests {
111
112 use super::*;
113
114 #[test]
115 fn no_jitter() {
116 let bo = ExponentialBackoffBuilder::default()
117 .jitter(Duration::default())
118 .max(Duration::from_secs(5))
119 .build();
120
121 assert_eq!(bo.duration(0), Duration::from_millis(500));
122 assert_eq!(bo.duration(1), Duration::from_millis(875));
123 assert_eq!(bo.duration(2), Duration::from_nanos(1531250000));
124 assert_eq!(bo.duration(3), Duration::from_nanos(2679687500));
125 assert_eq!(bo.duration(4), Duration::from_nanos(4689453125));
126 assert_eq!(bo.duration(5), Duration::from_secs(5));
127 }
128
129 #[test]
130 fn with_jitter() {
131 let bo = ExponentialBackoffBuilder::default()
132 .jitter(Duration::default())
133 .max(Duration::from_secs(5))
134 .build();
135
136 assert!(bo.duration(0) <= Duration::from_millis(500));
137 assert!(bo.duration(1) <= Duration::from_millis(875));
138 assert!(bo.duration(2) <= Duration::from_nanos(1531250000));
139 assert!(bo.duration(3) <= Duration::from_nanos(2679687500));
140 assert!(bo.duration(4) <= Duration::from_nanos(4689453125));
141 assert!(bo.duration(5) <= Duration::from_secs(5));
142 }
143}