error_forge/recovery/
backoff.rs1use std::time::Duration;
2use std::cmp::min;
3use rand::Rng;
4
5pub trait Backoff: Send + Sync + 'static {
7 fn next_delay(&self, attempt: usize) -> Duration;
9
10 fn reset(&mut self) {}
12
13 fn box_clone(&self) -> Box<dyn Backoff>;
15}
16
17#[derive(Clone)]
22pub struct ExponentialBackoff {
23 initial_delay_ms: u64,
24 max_delay_ms: u64,
25 factor: f64,
26 jitter: bool,
27}
28
29impl ExponentialBackoff {
30 pub fn new() -> Self {
38 Self {
39 initial_delay_ms: 100,
40 max_delay_ms: 30000,
41 factor: 2.0,
42 jitter: false,
43 }
44 }
45
46 pub fn with_initial_delay(mut self, delay_ms: u64) -> Self {
48 self.initial_delay_ms = delay_ms;
49 self
50 }
51
52 pub fn with_max_delay(mut self, delay_ms: u64) -> Self {
54 self.max_delay_ms = delay_ms;
55 self
56 }
57
58 pub fn with_factor(mut self, factor: f64) -> Self {
60 self.factor = factor;
61 self
62 }
63
64 pub fn with_jitter(mut self, jitter: bool) -> Self {
66 self.jitter = jitter;
67 self
68 }
69}
70
71impl Backoff for ExponentialBackoff {
72 fn next_delay(&self, attempt: usize) -> Duration {
73 if attempt == 0 {
74 return Duration::from_millis(self.initial_delay_ms);
75 }
76
77 let exp_factor = self.factor.powi(attempt as i32);
79 let calculated_delay = (self.initial_delay_ms as f64 * exp_factor) as u64;
80 let capped_delay = min(calculated_delay, self.max_delay_ms);
81
82 if self.jitter {
83 let mut rng = rand::thread_rng();
85 let jitter_factor = rng.gen_range(0.8..1.2);
86 let jittered_delay = (capped_delay as f64 * jitter_factor) as u64;
87 Duration::from_millis(jittered_delay)
88 } else {
89 Duration::from_millis(capped_delay)
90 }
91 }
92
93 fn box_clone(&self) -> Box<dyn Backoff> {
94 Box::new(self.clone())
95 }
96}
97
98#[derive(Clone)]
102pub struct LinearBackoff {
103 initial_delay_ms: u64,
104 increment_ms: u64,
105 max_delay_ms: u64,
106}
107
108impl LinearBackoff {
109 pub fn new() -> Self {
116 Self {
117 initial_delay_ms: 100,
118 increment_ms: 100,
119 max_delay_ms: 10000,
120 }
121 }
122
123 pub fn with_initial_delay(mut self, delay_ms: u64) -> Self {
125 self.initial_delay_ms = delay_ms;
126 self
127 }
128
129 pub fn with_increment(mut self, increment_ms: u64) -> Self {
131 self.increment_ms = increment_ms;
132 self
133 }
134
135 pub fn with_max_delay(mut self, delay_ms: u64) -> Self {
137 self.max_delay_ms = delay_ms;
138 self
139 }
140}
141
142impl Backoff for LinearBackoff {
143 fn next_delay(&self, attempt: usize) -> Duration {
144 let delay_ms = self.initial_delay_ms + (attempt as u64 * self.increment_ms);
145 let capped_delay = min(delay_ms, self.max_delay_ms);
146 Duration::from_millis(capped_delay)
147 }
148
149 fn box_clone(&self) -> Box<dyn Backoff> {
150 Box::new(self.clone())
151 }
152}
153
154#[derive(Clone)]
158pub struct FixedBackoff {
159 delay_ms: u64,
160}
161
162impl FixedBackoff {
163 pub fn new(delay_ms: u64) -> Self {
165 Self { delay_ms }
166 }
167}
168
169impl Backoff for FixedBackoff {
170 fn next_delay(&self, _attempt: usize) -> Duration {
171 Duration::from_millis(self.delay_ms)
172 }
173
174 fn box_clone(&self) -> Box<dyn Backoff> {
175 Box::new(self.clone())
176 }
177}
178
179impl Default for ExponentialBackoff {
180 fn default() -> Self {
181 Self::new()
182 }
183}
184
185impl Default for LinearBackoff {
186 fn default() -> Self {
187 Self::new()
188 }
189}
190
191impl Backoff for Box<dyn Backoff> {
193 fn next_delay(&self, attempt: usize) -> Duration {
194 (**self).next_delay(attempt)
195 }
196
197 fn reset(&mut self) {
198 (**self).reset()
199 }
200
201 fn box_clone(&self) -> Box<dyn Backoff> {
202 (**self).box_clone()
203 }
204}