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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use std::time::Duration;
use instant::Instant;
use rand;
use crate::default;
use crate::backoff::Backoff;
use crate::clock::Clock;
#[derive(Debug)]
pub struct ExponentialBackoff<C> {
pub current_interval: Duration,
pub initial_interval: Duration,
pub randomization_factor: f64,
pub multiplier: f64,
pub max_interval: Duration,
pub start_time: Instant,
pub max_elapsed_time: Option<Duration>,
pub clock: C,
}
impl<C> Default for ExponentialBackoff<C>
where C: Clock + Default
{
fn default() -> ExponentialBackoff<C> {
let mut eb = ExponentialBackoff {
current_interval: Duration::from_millis(default::INITIAL_INTERVAL_MILLIS),
initial_interval: Duration::from_millis(default::INITIAL_INTERVAL_MILLIS),
randomization_factor: default::RANDOMIZATION_FACTOR,
multiplier: default::MULTIPLIER,
max_interval: Duration::from_millis(default::MAX_INTERVAL_MILLIS),
max_elapsed_time: Some(Duration::from_millis(default::MAX_ELAPSED_TIME_MILLIS)),
clock: C::default(),
start_time: Instant::now(),
};
eb.reset();
eb
}
}
impl<C: Clock> ExponentialBackoff<C> {
pub fn get_elapsed_time(&self) -> Duration {
self.clock.now().duration_since(self.start_time)
}
fn get_random_value_from_interval(randomization_factor: f64,
random: f64,
current_interval: Duration)
-> Duration {
let current_interval_nanos = duration_to_nanos(current_interval);
let delta = randomization_factor * current_interval_nanos;
let min_interval = current_interval_nanos - delta;
let max_interval = current_interval_nanos + delta;
let diff = max_interval - min_interval;
let nanos = min_interval + (random * (diff + 1.0));
nanos_to_duration(nanos)
}
fn increment_current_interval(&mut self) -> Duration {
let current_interval_nanos = duration_to_nanos(self.current_interval);
let max_interval_nanos = duration_to_nanos(self.max_interval);
if current_interval_nanos >= max_interval_nanos / self.multiplier {
self.max_interval
} else {
let nanos = current_interval_nanos * self.multiplier;
nanos_to_duration(nanos)
}
}
}
fn duration_to_nanos(d: Duration) -> f64 {
d.as_secs() as f64 * 1_000_000_000.0 + f64::from(d.subsec_nanos())
}
fn nanos_to_duration(nanos: f64) -> Duration {
let secs = nanos / 1_000_000_000.0;
let nanos = nanos as u64 % 1_000_000_000;
Duration::new(secs as u64, nanos as u32)
}
impl<C> Backoff for ExponentialBackoff<C>
where C: Clock
{
fn reset(&mut self) {
self.current_interval = self.initial_interval;
self.start_time = self.clock.now();
}
fn next_backoff(&mut self) -> Option<Duration> {
match self.max_elapsed_time {
Some(v) if self.get_elapsed_time() > v => None,
_ => {
let random = rand::random::<f64>();
let randomized_interval =
Self::get_random_value_from_interval(self.randomization_factor,
random,
self.current_interval);
self.current_interval = self.increment_current_interval();
Some(randomized_interval)
}
}
}
}
#[cfg(test)]
use crate::clock::SystemClock;
#[test]
fn get_randomized_interval() {
let f = ExponentialBackoff::<SystemClock>::get_random_value_from_interval;
assert_eq!(Duration::new(0, 1), f(0.5, 0.0, Duration::new(0, 2)));
assert_eq!(Duration::new(0, 1), f(0.5, 0.33, Duration::new(0, 2)));
assert_eq!(Duration::new(0, 2), f(0.5, 0.34, Duration::new(0, 2)));
assert_eq!(Duration::new(0, 2), f(0.5, 0.66, Duration::new(0, 2)));
assert_eq!(Duration::new(0, 3), f(0.5, 0.67, Duration::new(0, 2)));
assert_eq!(Duration::new(0, 3), f(0.5, 0.99, Duration::new(0, 2)));
}