feat(supervisor): exponential backoff calculator

This commit is contained in:
2026-05-25 11:34:53 +02:00
parent 4837a73167
commit 54045da2df
+70
View File
@@ -0,0 +1,70 @@
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct Backoff {
initial: Duration,
max: Duration,
current: Option<Duration>,
}
impl Backoff {
pub fn new(initial: Duration, max: Duration) -> Self {
Self {
initial,
max,
current: None,
}
}
pub fn next(&mut self) -> Duration {
let next = match self.current {
None => self.initial,
Some(d) => (d * 2).min(self.max),
};
self.current = Some(next);
next
}
pub fn reset(&mut self) {
self.current = None;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn starts_at_initial() {
let mut b = Backoff::new(Duration::from_secs(1), Duration::from_secs(30));
assert_eq!(b.next(), Duration::from_secs(1));
}
#[test]
fn doubles_each_call() {
let mut b = Backoff::new(Duration::from_secs(1), Duration::from_secs(30));
assert_eq!(b.next(), Duration::from_secs(1));
assert_eq!(b.next(), Duration::from_secs(2));
assert_eq!(b.next(), Duration::from_secs(4));
assert_eq!(b.next(), Duration::from_secs(8));
}
#[test]
fn caps_at_max() {
let mut b = Backoff::new(Duration::from_secs(1), Duration::from_secs(5));
for _ in 0..10 {
b.next();
}
assert_eq!(b.next(), Duration::from_secs(5));
}
#[test]
fn reset_starts_over() {
let mut b = Backoff::new(Duration::from_secs(1), Duration::from_secs(30));
b.next();
b.next();
b.next();
b.reset();
assert_eq!(b.next(), Duration::from_secs(1));
}
}