-
Notifications
You must be signed in to change notification settings - Fork 0
/
backoff.rs
114 lines (94 loc) · 2.88 KB
/
backoff.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use std::time::Duration;
use super::errors::*;
/// The Backoff trait provides a method to return
/// how long to back off when called.
pub trait Backoff {
/// wait for the next backoff duration. Each backoff
/// function will vary in how the duration changes with time.
fn wait(&mut self) -> Duration;
/// reset the backoff duration to the initial if it has some
/// running state.
fn reset(&mut self);
}
#[derive(Debug, Default, Clone)]
pub struct ExponentialBackoff {
factor: f64,
max: Option<f64>,
min: f64,
hits: i32,
}
impl Backoff for ExponentialBackoff {
fn wait(&mut self) -> Duration {
let mut secs = self.factor.powi(self.hits);
self.hits += 1;
secs = secs.max(self.min);
if let Some(max) = self.max {
secs = secs.min(max)
}
Duration::from_secs_f64(secs)
}
fn reset(&mut self) {
self.hits = 1;
}
}
pub trait BackoffBuilder<T: Backoff> {
fn build(&mut self) -> Result<T>;
}
#[derive(Default, Debug)]
pub struct ExponentialBackoffBuilder {
min: Option<Duration>,
max: Option<Duration>,
factor: Option<f64>,
}
impl BackoffBuilder<ExponentialBackoff> for ExponentialBackoffBuilder {
/// Build finishes the exponential backoff and returns it or an error.
fn build(&mut self) -> Result<ExponentialBackoff> {
Ok(ExponentialBackoff {
min: self.min.ok_or_else(|| format_err!("the minimum initial value is required"))?.as_secs_f64(),
max: self.max.map(|s| s.as_secs_f64()),
hits: 1,
factor: self.factor.unwrap_or(2.0),
..ExponentialBackoff::default()
})
}
}
impl ExponentialBackoffBuilder {
/// The minimum and initial delay of the backoff.
pub fn min(&mut self, min: Duration) -> &mut Self {
self.min = Some(min);
self
}
/// The capped maximum delay that can be returned.
pub fn max(&mut self, max: Duration) -> &mut Self {
self.max = Some(max);
self
}
/// The factor that the delay increases by on each hit. Defaults to 2.0.
pub fn factor(&mut self, f: f64) -> &mut Self {
self.factor = Some(f);
self
}
}
#[test]
fn test_exp_backoff() {
let backoff = ExponentialBackoffBuilder::default()
.min(Duration::from_secs_f64(0.0))
.max(Duration::from_secs_f64((2.0 as f64).powi(20)))
.factor(2.0)
.build();
assert!(backoff.is_ok(), "{:?}", backoff.err());
let mut backoff = backoff.unwrap();
for i in 1..20 {
let delay = backoff.wait();
let ex = Duration::from_secs_f64((2.0 as f64).powi(i));
assert!(ex == delay, "on iter {}: {:?} != {:?}", i, ex, delay);
}
backoff.reset();
let delay = backoff.wait();
assert!(
delay == Duration::new(2, 0),
"after reset: {:?} != {:?}",
Duration::new(2, 0),
delay
);
}