error_forge/recovery/
backoff.rs1#[cfg(feature = "jitter")]
2use rand::Rng;
3use std::cmp::min;
4use std::time::Duration;
5
6pub trait Backoff: Send + Sync + 'static {
8 fn next_delay(&self, attempt: usize) -> Duration;
10
11 fn reset(&mut self) {}
13
14 fn box_clone(&self) -> Box<dyn Backoff>;
16}
17
18#[derive(Clone)]
23pub struct ExponentialBackoff {
24 initial_delay_ms: u64,
25 max_delay_ms: u64,
26 factor: f64,
27 jitter: bool,
28}
29
30impl ExponentialBackoff {
31 pub fn new() -> Self {
39 Self {
40 initial_delay_ms: 100,
41 max_delay_ms: 30000,
42 factor: 2.0,
43 jitter: false,
44 }
45 }
46
47 pub fn with_initial_delay(mut self, delay_ms: u64) -> Self {
49 self.initial_delay_ms = delay_ms;
50 self
51 }
52
53 pub fn with_max_delay(mut self, delay_ms: u64) -> Self {
55 self.max_delay_ms = delay_ms;
56 self
57 }
58
59 pub fn with_factor(mut self, factor: f64) -> Self {
61 self.factor = factor;
62 self
63 }
64
65 pub fn with_jitter(mut self, jitter: bool) -> Self {
71 self.jitter = jitter;
72 self
73 }
74}
75
76impl Backoff for ExponentialBackoff {
77 fn next_delay(&self, attempt: usize) -> Duration {
78 if attempt == 0 {
79 return Duration::from_millis(self.initial_delay_ms);
80 }
81
82 let exp_factor = self.factor.powi(attempt as i32);
84 let calculated_delay = (self.initial_delay_ms as f64 * exp_factor) as u64;
85 let capped_delay = min(calculated_delay, self.max_delay_ms);
86
87 #[cfg(feature = "jitter")]
92 if self.jitter {
93 let mut rng = rand::thread_rng();
94 let jitter_factor = rng.gen_range(0.8..1.2);
95 let jittered_delay = (capped_delay as f64 * jitter_factor) as u64;
96 return Duration::from_millis(jittered_delay);
97 }
98
99 Duration::from_millis(capped_delay)
100 }
101
102 fn box_clone(&self) -> Box<dyn Backoff> {
103 Box::new(self.clone())
104 }
105}
106
107#[derive(Clone)]
111pub struct LinearBackoff {
112 initial_delay_ms: u64,
113 increment_ms: u64,
114 max_delay_ms: u64,
115}
116
117impl LinearBackoff {
118 pub fn new() -> Self {
125 Self {
126 initial_delay_ms: 100,
127 increment_ms: 100,
128 max_delay_ms: 10000,
129 }
130 }
131
132 pub fn with_initial_delay(mut self, delay_ms: u64) -> Self {
134 self.initial_delay_ms = delay_ms;
135 self
136 }
137
138 pub fn with_increment(mut self, increment_ms: u64) -> Self {
140 self.increment_ms = increment_ms;
141 self
142 }
143
144 pub fn with_max_delay(mut self, delay_ms: u64) -> Self {
146 self.max_delay_ms = delay_ms;
147 self
148 }
149}
150
151impl Backoff for LinearBackoff {
152 fn next_delay(&self, attempt: usize) -> Duration {
153 let delay_ms = self.initial_delay_ms + (attempt as u64 * self.increment_ms);
154 let capped_delay = min(delay_ms, self.max_delay_ms);
155 Duration::from_millis(capped_delay)
156 }
157
158 fn box_clone(&self) -> Box<dyn Backoff> {
159 Box::new(self.clone())
160 }
161}
162
163#[derive(Clone)]
167pub struct FixedBackoff {
168 delay_ms: u64,
169}
170
171impl FixedBackoff {
172 pub fn new(delay_ms: u64) -> Self {
174 Self { delay_ms }
175 }
176}
177
178impl Backoff for FixedBackoff {
179 fn next_delay(&self, _attempt: usize) -> Duration {
180 Duration::from_millis(self.delay_ms)
181 }
182
183 fn box_clone(&self) -> Box<dyn Backoff> {
184 Box::new(self.clone())
185 }
186}
187
188impl Default for ExponentialBackoff {
189 fn default() -> Self {
190 Self::new()
191 }
192}
193
194impl Default for LinearBackoff {
195 fn default() -> Self {
196 Self::new()
197 }
198}
199
200impl Backoff for Box<dyn Backoff> {
202 fn next_delay(&self, attempt: usize) -> Duration {
203 (**self).next_delay(attempt)
204 }
205
206 fn reset(&mut self) {
207 (**self).reset()
208 }
209
210 fn box_clone(&self) -> Box<dyn Backoff> {
211 (**self).box_clone()
212 }
213}