⎇ 545dd59c727b2c6b9752ab4b29de64ad0d24b35c
crates/runtime/sandokan-lifecycle/src/backoff.rs
Historia
Quién tocó qué
crudo
feat(sandokan-lifecycle): A4 — primitivas de lifecycle agnósticas
1//! Backoff exponencial con tope.
2
3use std::time::Duration;
4
5/// Calculador de backoff exponencial. Cada `next_delay()` devuelve el
6/// delay actual y luego lo duplica, hasta saturar en `max`.
7#[derive(Debug, Clone)]
8pub struct Backoff {
9 base: Duration,
10 max: Duration,
11 current: Duration,
12}
13
14impl Backoff {
15 /// Crea un backoff que arranca en `base` y satura en `max`.
16 /// Si `base > max`, `base` se clampa a `max`.
17 pub fn new(base: Duration, max: Duration) -> Self {
18 let base = base.min(max);
19 Self { base, max, current: base }
20 }
21
22 /// Devuelve el delay actual y escala el siguiente (×2, capeado a `max`).
23 pub fn next_delay(&mut self) -> Duration {
24 let delay = self.current;
25 self.current = (self.current * 2).min(self.max);
26 delay
27 }
28
29 /// Vuelve al delay base (tras un éxito).
30 pub fn reset(&mut self) {
31 self.current = self.base;
32 }
33
34 /// Delay que devolvería el próximo `next_delay()` sin consumirlo.
35 pub fn peek(&self) -> Duration {
36 self.current
37 }
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[test]
45 fn escalates_then_caps() {
46 let mut b = Backoff::new(Duration::from_millis(100), Duration::from_millis(800));
47 assert_eq!(b.next_delay(), Duration::from_millis(100));
48 assert_eq!(b.next_delay(), Duration::from_millis(200));
49 assert_eq!(b.next_delay(), Duration::from_millis(400));
50 assert_eq!(b.next_delay(), Duration::from_millis(800));
51 assert_eq!(b.next_delay(), Duration::from_millis(800)); // capeado
52 }
53
54 #[test]
55 fn reset_returns_to_base() {
56 let mut b = Backoff::new(Duration::from_millis(100), Duration::from_secs(30));
57 b.next_delay();
58 b.next_delay();
59 b.reset();
60 assert_eq!(b.next_delay(), Duration::from_millis(100));
61 }
62
63 #[test]
64 fn base_clamped_to_max() {
65 let mut b = Backoff::new(Duration::from_secs(10), Duration::from_secs(1));
66 assert_eq!(b.next_delay(), Duration::from_secs(1));
67 }
68}