Circuit Breaker State Machine
Track failures with closed, open, and half-open states so a failing dependency is rejected fast until a cooldown trial succeeds.
Code
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State {
Closed,
Open,
HalfOpen,
}
struct CircuitBreaker {
state: State,
failures: u32,
threshold: u32,
cooldown: Duration,
opened_at: Instant,
}
enum Decision {
Allow,
Reject,
}
impl CircuitBreaker {
fn new(threshold: u32, cooldown: Duration) -> Self {
Self {
state: State::Closed,
failures: 0,
threshold,
cooldown,
opened_at: Instant::now(),
}
}
fn allow(&mut self) -> Decision {
match self.state {
State::Closed => Decision::Allow,
State::Open if self.opened_at.elapsed() >= self.cooldown => {
self.state = State::HalfOpen;
Decision::Allow
}
State::Open => Decision::Reject,
State::HalfOpen => Decision::Reject,
}
}
fn record(&mut self, success: bool) {
if success {
self.state = State::Closed;
self.failures = 0;
return;
}
self.failures += 1;
if self.failures >= self.threshold || self.state == State::HalfOpen {
self.state = State::Open;
self.opened_at = Instant::now();
}
}
}
fn main() {
let mut breaker = CircuitBreaker::new(2, Duration::from_millis(50));
for attempt in 0..4 {
let allowed = matches!(breaker.allow(), Decision::Allow);
println!("attempt {attempt}: allowed={allowed}");
if allowed {
breaker.record(false);
}
}
std::thread::sleep(Duration::from_millis(60));
let trial = matches!(breaker.allow(), Decision::Allow);
println!("after cooldown allowed={trial}");
breaker.record(true);
}
Report a problem with this exampleOpens a pre-filled issue on GitHub.