vault_client_rs/
circuit_breaker.rs1use std::sync::Mutex;
2use std::time::{Duration, Instant};
3
4use crate::types::error::VaultError;
5
6#[derive(Debug, Clone)]
12pub struct CircuitBreakerConfig {
13 pub failure_threshold: u32,
15 pub reset_timeout: Duration,
17}
18
19impl Default for CircuitBreakerConfig {
20 fn default() -> Self {
21 Self {
22 failure_threshold: 5,
23 reset_timeout: Duration::from_secs(30),
24 }
25 }
26}
27
28enum State {
29 Closed { consecutive_failures: u32 },
30 Open { since: Instant },
31 HalfOpen,
32}
33
34pub(crate) struct CircuitBreaker {
35 config: CircuitBreakerConfig,
36 state: Mutex<State>,
37}
38
39impl CircuitBreaker {
40 pub fn new(config: CircuitBreakerConfig) -> Self {
41 Self {
42 config,
43 state: Mutex::new(State::Closed {
44 consecutive_failures: 0,
45 }),
46 }
47 }
48
49 pub fn check(&self) -> Result<(), VaultError> {
52 let mut state = self.state.lock().map_err(|_| VaultError::LockPoisoned)?;
53 match *state {
54 State::Closed { .. } => Ok(()),
55 State::Open { since } => {
56 if since.elapsed() >= self.config.reset_timeout {
57 *state = State::HalfOpen;
58 Ok(())
59 } else {
60 Err(VaultError::CircuitOpen)
61 }
62 }
63 State::HalfOpen => {
64 Err(VaultError::CircuitOpen)
67 }
68 }
69 }
70
71 pub fn record_success(&self) {
73 if let Ok(mut state) = self.state.lock() {
74 *state = State::Closed {
75 consecutive_failures: 0,
76 };
77 }
78 }
79
80 pub fn record_failure(&self) {
83 if let Ok(mut state) = self.state.lock() {
84 match *state {
85 State::Closed {
86 consecutive_failures,
87 } => {
88 let new_count = consecutive_failures + 1;
89 if new_count >= self.config.failure_threshold {
90 *state = State::Open {
91 since: Instant::now(),
92 };
93 } else {
94 *state = State::Closed {
95 consecutive_failures: new_count,
96 };
97 }
98 }
99 State::HalfOpen => {
100 *state = State::Open {
102 since: Instant::now(),
103 };
104 }
105 State::Open { .. } => {
106 }
108 }
109 }
110 }
111}