faucet_core/resilience/
breaker.rs1#[derive(Debug)]
6pub struct CircuitBreaker {
7 threshold: u32,
8 consecutive: u32,
9}
10
11impl CircuitBreaker {
12 pub fn new(threshold: u32) -> Self {
14 Self {
15 threshold: threshold.max(1),
16 consecutive: 0,
17 }
18 }
19
20 pub fn record_success(&mut self) {
22 self.consecutive = 0;
23 }
24
25 pub fn record_failure(&mut self) -> bool {
27 self.consecutive = self.consecutive.saturating_add(1);
28 self.consecutive >= self.threshold
29 }
30
31 pub fn consecutive(&self) -> u32 {
33 self.consecutive
34 }
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40
41 #[test]
42 fn opens_after_threshold_consecutive_failures() {
43 let mut b = CircuitBreaker::new(3);
44 assert!(!b.record_failure());
45 assert!(!b.record_failure());
46 assert!(b.record_failure(), "third consecutive failure should open");
47 assert_eq!(b.consecutive(), 3);
48 }
49
50 #[test]
51 fn success_resets_the_counter() {
52 let mut b = CircuitBreaker::new(3);
53 b.record_failure();
54 b.record_failure();
55 b.record_success();
56 assert_eq!(b.consecutive(), 0);
57 assert!(!b.record_failure());
58 }
59
60 #[test]
61 fn threshold_one_opens_immediately() {
62 let mut b = CircuitBreaker::new(1);
63 assert!(b.record_failure());
64 }
65}