Skip to main content

camel_api/
supervision.rs

1//! Supervision configuration for automatic route restart.
2
3use std::time::Duration;
4
5/// Configuration for route supervision (automatic restart on crash).
6#[derive(Debug, Clone, PartialEq)]
7pub struct SupervisionConfig {
8    /// Maximum number of restart attempts. `None` means infinite retries.
9    /// Default: `Some(5)`.
10    pub max_attempts: Option<u32>,
11
12    /// Delay before the first restart attempt.
13    /// Default: 1 second.
14    pub initial_delay: Duration,
15
16    /// Multiplier applied to the delay after each failed attempt.
17    /// Default: 2.0 (doubles each time).
18    pub backoff_multiplier: f64,
19
20    /// Maximum delay cap between restart attempts.
21    /// Default: 60 seconds.
22    pub max_delay: Duration,
23}
24
25impl SupervisionConfig {
26    /// Compute the delay before attempt number `attempt` (1-indexed).
27    ///
28    /// Formula: `min(initial_delay * backoff_multiplier^(attempt-1), max_delay)`
29    pub fn next_delay(&self, attempt: u32) -> Duration {
30        if attempt == 0 {
31            return self.initial_delay;
32        }
33        // Cap exponent to prevent overflow with very large attempt counts
34        let exp = ((attempt - 1) as i32).min(63);
35        let exp_value = self.backoff_multiplier.powi(exp);
36        let millis = (self.initial_delay.as_millis() as f64 * exp_value) as u128;
37        let delay = Duration::from_millis(millis as u64);
38        delay.min(self.max_delay)
39    }
40}
41
42impl Default for SupervisionConfig {
43    fn default() -> Self {
44        Self {
45            max_attempts: Some(5),
46            initial_delay: Duration::from_secs(1),
47            backoff_multiplier: 2.0,
48            max_delay: Duration::from_secs(60),
49        }
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use std::time::Duration;
57
58    #[test]
59    fn test_supervision_config_defaults() {
60        let cfg = SupervisionConfig::default();
61        assert_eq!(cfg.max_attempts, Some(5));
62        assert_eq!(cfg.initial_delay, Duration::from_secs(1));
63        assert_eq!(cfg.backoff_multiplier, 2.0);
64        assert_eq!(cfg.max_delay, Duration::from_secs(60));
65    }
66
67    #[test]
68    fn test_supervision_config_infinite() {
69        let cfg = SupervisionConfig {
70            max_attempts: None,
71            ..Default::default()
72        };
73        assert!(cfg.max_attempts.is_none());
74    }
75
76    #[test]
77    fn test_supervision_config_next_delay_growth() {
78        let cfg = SupervisionConfig::default();
79        let d1 = cfg.next_delay(1); // attempt 1 → 1s * 2^0 = 1s
80        let d2 = cfg.next_delay(2); // attempt 2 → 1s * 2^1 = 2s
81        let d3 = cfg.next_delay(3); // attempt 3 → 1s * 2^2 = 4s
82        assert_eq!(d1, Duration::from_secs(1));
83        assert_eq!(d2, Duration::from_secs(2));
84        assert_eq!(d3, Duration::from_secs(4));
85    }
86
87    #[test]
88    fn test_supervision_config_next_delay_capped() {
89        let cfg = SupervisionConfig {
90            max_delay: Duration::from_secs(5),
91            ..Default::default()
92        };
93        let d = cfg.next_delay(10); // would be 512s, capped at 5s
94        assert_eq!(d, Duration::from_secs(5));
95    }
96
97    #[test]
98    fn test_next_delay_attempt_zero_returns_initial_delay() {
99        let cfg = SupervisionConfig::default();
100        let d = cfg.next_delay(0);
101        assert_eq!(d, cfg.initial_delay);
102    }
103}