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}