Skip to main content

camel_api/
circuit_breaker.rs

1use std::time::Duration;
2
3/// Configuration for the circuit breaker pattern.
4///
5/// The circuit breaker monitors failures and temporarily stops sending
6/// requests to a failing service, giving it time to recover.
7///
8/// # States
9///
10/// - **Closed** — Normal operation. Failures are counted; when `failure_threshold`
11///   consecutive failures occur, the circuit opens.
12/// - **Open** — All calls are rejected with [`CamelError::CircuitOpen`](crate::CamelError::CircuitOpen).
13///   After `open_duration` elapses, the circuit transitions to half-open.
14/// - **Half-Open** — A single probe call is allowed through. If it succeeds
15///   (`success_threshold` times), the circuit closes. If it fails, the circuit reopens.
16#[derive(Debug, Clone)]
17pub struct CircuitBreakerConfig {
18    /// Number of consecutive failures before opening the circuit.
19    pub failure_threshold: u32,
20    /// Number of successful probes in half-open state before closing.
21    ///
22    /// **Current limitation:** Only `1` is effectively supported. The state
23    /// machine resets to `Closed` on the first successful half-open probe
24    /// regardless of this value. Multi-probe half-open tracking is deferred.
25    pub success_threshold: u32,
26    /// How long the circuit stays open before allowing a probe.
27    pub open_duration: Duration,
28}
29
30impl Default for CircuitBreakerConfig {
31    fn default() -> Self {
32        Self {
33            failure_threshold: 5,
34            success_threshold: 1,
35            open_duration: Duration::from_secs(30),
36        }
37    }
38}
39
40impl CircuitBreakerConfig {
41    pub fn new() -> Self {
42        Self::default()
43    }
44
45    pub fn failure_threshold(mut self, n: u32) -> Self {
46        self.failure_threshold = n;
47        self
48    }
49
50    pub fn open_duration(mut self, duration: Duration) -> Self {
51        self.open_duration = duration;
52        self
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_default_config() {
62        let config = CircuitBreakerConfig::default();
63        assert_eq!(config.failure_threshold, 5);
64        assert_eq!(config.success_threshold, 1);
65        assert_eq!(config.open_duration, Duration::from_secs(30));
66    }
67
68    #[test]
69    fn test_builder_pattern() {
70        let config = CircuitBreakerConfig::new()
71            .failure_threshold(3)
72            .open_duration(Duration::from_millis(500));
73        assert_eq!(config.failure_threshold, 3);
74        assert_eq!(config.success_threshold, 1); // default, no setter exposed
75        assert_eq!(config.open_duration, Duration::from_millis(500));
76    }
77}