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
use std::time::Duration;
use rand::prelude::*;
const NEW_CONN_BACKOFFS_MS: &[Duration] = &[
Duration::from_millis(5),
Duration::from_millis(10),
Duration::from_millis(10),
Duration::from_millis(15),
Duration::from_millis(30),
Duration::from_millis(30),
Duration::from_millis(50),
Duration::from_millis(50),
Duration::from_millis(100),
Duration::from_millis(100),
Duration::from_millis(100),
Duration::from_millis(200),
Duration::from_millis(300),
Duration::from_millis(500),
Duration::from_millis(500),
Duration::from_millis(1_000),
Duration::from_millis(1_000),
Duration::from_millis(1_000),
Duration::from_millis(2_500),
Duration::from_millis(2_500),
Duration::from_millis(5_000),
Duration::from_millis(5_000),
Duration::from_millis(5_000),
Duration::from_millis(7_500),
Duration::from_millis(7_500),
Duration::from_millis(10_000),
];
const MAX_NEW_CONN_BACKOFF_MS: Duration = Duration::from_millis(10_000);
#[derive(Debug, Clone, Copy)]
pub enum BackoffStrategy {
NoBackoff,
Constant { fixed: Duration, jitter: bool },
Incremental { jitter: bool },
IncrementalCapped { cap: Duration, jitter: bool },
}
impl BackoffStrategy {
pub(crate) fn get_next_backoff(&self, attempt: usize) -> Option<Duration> {
fn calc_backoff(attempt: usize) -> Duration {
let idx = (if attempt == 0 { 0 } else { attempt - 1 }) as usize;
if idx < NEW_CONN_BACKOFFS_MS.len() {
NEW_CONN_BACKOFFS_MS[idx]
} else {
MAX_NEW_CONN_BACKOFF_MS
}
}
let (backoff, with_jitter) = match self {
BackoffStrategy::NoBackoff => return None,
BackoffStrategy::Constant { fixed, jitter } => (*fixed, *jitter),
BackoffStrategy::Incremental { jitter } => (calc_backoff(attempt), *jitter),
BackoffStrategy::IncrementalCapped { cap, jitter } => {
let uncapped = calc_backoff(attempt);
let effective = std::cmp::min(uncapped, *cap);
(effective, *jitter)
}
};
if with_jitter {
let ms = backoff.as_millis() as u64;
let effective_jitter = if ms >= 100 {
let twenty_percent = ms / 5;
std::cmp::min(twenty_percent, 3_000)
} else if ms == 1 {
1
} else {
ms / 3
};
if effective_jitter != 0 {
let mut rng = rand::thread_rng();
let jitter = rng.gen_range(0, effective_jitter);
Some(backoff + Duration::from_millis(jitter))
} else {
Some(backoff)
}
} else {
Some(backoff)
}
}
}
impl Default for BackoffStrategy {
fn default() -> Self {
BackoffStrategy::IncrementalCapped {
cap: Duration::from_secs(10),
jitter: true,
}
}
}