#[ cfg( feature = "circuit_breaker" ) ]
mod private
{
use core::time::Duration;
use std::sync::{ Arc, Mutex };
use std::time::Instant;
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
pub enum CircuitBreakerState
{
Closed,
Open,
HalfOpen,
}
#[ derive( Debug, Clone ) ]
pub struct CircuitBreakerConfig
{
failure_threshold : u32,
recovery_timeout : Duration,
half_open_max_calls : u32,
}
#[ derive( Debug ) ]
pub struct CircuitBreaker
{
config : CircuitBreakerConfig,
state : Arc< Mutex< CircuitBreakerInternalState > >,
}
#[ derive( Debug ) ]
struct CircuitBreakerInternalState
{
current_state : CircuitBreakerState,
failure_count : u32,
last_failure_time : Option< Instant >,
half_open_calls : u32,
}
impl CircuitBreakerConfig
{
#[ inline ]
#[ must_use ]
pub fn new() -> Self
{
Self
{
failure_threshold : 5,
recovery_timeout : Duration::from_secs( 60 ),
half_open_max_calls : 3,
}
}
#[ inline ]
#[ must_use ]
pub fn with_failure_threshold( mut self, threshold : u32 ) -> Self
{
self.failure_threshold = threshold;
self
}
#[ inline ]
#[ must_use ]
pub fn with_recovery_timeout( mut self, timeout : Duration ) -> Self
{
self.recovery_timeout = timeout;
self
}
#[ inline ]
#[ must_use ]
pub fn with_half_open_max_calls( mut self, max_calls : u32 ) -> Self
{
self.half_open_max_calls = max_calls;
self
}
#[ inline ]
#[ must_use ]
pub fn failure_threshold( &self ) -> u32
{
self.failure_threshold
}
#[ inline ]
#[ must_use ]
pub fn recovery_timeout( &self ) -> Duration
{
self.recovery_timeout
}
#[ inline ]
#[ must_use ]
pub fn half_open_max_calls( &self ) -> u32
{
self.half_open_max_calls
}
}
impl Default for CircuitBreakerConfig
{
#[ inline ]
fn default() -> Self
{
Self::new()
}
}
impl CircuitBreaker
{
#[ inline ]
#[ must_use ]
pub fn new( config : CircuitBreakerConfig ) -> Self
{
Self
{
config,
state : Arc::new( Mutex::new( CircuitBreakerInternalState
{
current_state : CircuitBreakerState::Closed,
failure_count : 0,
last_failure_time : None,
half_open_calls : 0,
})),
}
}
#[ inline ]
#[ must_use ]
pub fn can_execute( &self ) -> bool
{
let mut state = self.state.lock().unwrap();
match state.current_state
{
CircuitBreakerState::Closed => true,
CircuitBreakerState::Open =>
{
if let Some( last_failure ) = state.last_failure_time
{
if last_failure.elapsed() >= self.config.recovery_timeout
{
state.current_state = CircuitBreakerState::HalfOpen;
state.half_open_calls = 0;
return true;
}
}
false
},
CircuitBreakerState::HalfOpen =>
{
state.half_open_calls < self.config.half_open_max_calls
},
}
}
#[ inline ]
pub fn record_success( &self )
{
let mut state = self.state.lock().unwrap();
match state.current_state
{
CircuitBreakerState::Closed =>
{
state.failure_count = 0;
},
CircuitBreakerState::HalfOpen =>
{
state.half_open_calls += 1;
if state.half_open_calls >= self.config.half_open_max_calls
{
state.current_state = CircuitBreakerState::Closed;
state.failure_count = 0;
state.last_failure_time = None;
state.half_open_calls = 0;
}
},
CircuitBreakerState::Open =>
{
state.current_state = CircuitBreakerState::Closed;
state.failure_count = 0;
state.last_failure_time = None;
},
}
}
#[ inline ]
pub fn record_failure( &self )
{
let mut state = self.state.lock().unwrap();
match state.current_state
{
CircuitBreakerState::Closed =>
{
state.failure_count += 1;
state.last_failure_time = Some( Instant::now() );
if state.failure_count >= self.config.failure_threshold
{
state.current_state = CircuitBreakerState::Open;
}
},
CircuitBreakerState::HalfOpen =>
{
state.current_state = CircuitBreakerState::Open;
state.failure_count += 1;
state.last_failure_time = Some( Instant::now() );
state.half_open_calls = 0;
},
CircuitBreakerState::Open =>
{
state.last_failure_time = Some( Instant::now() );
},
}
}
#[ inline ]
#[ must_use ]
pub fn state( &self ) -> CircuitBreakerState
{
let state = self.state.lock().unwrap();
match state.current_state
{
CircuitBreakerState::Open =>
{
if let Some( last_failure ) = state.last_failure_time
{
if last_failure.elapsed() >= self.config.recovery_timeout
{
return CircuitBreakerState::HalfOpen;
}
}
},
_ => {},
}
state.current_state
}
#[ inline ]
#[ must_use ]
pub fn failure_count( &self ) -> u32
{
let state = self.state.lock().unwrap();
state.failure_count
}
}
impl Clone for CircuitBreaker
{
#[ inline ]
fn clone( &self ) -> Self
{
Self
{
config : self.config.clone(),
state : Arc::clone( &self.state ),
}
}
}
impl core::fmt::Display for CircuitBreaker
{
#[ inline ]
fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result
{
let state = self.state.lock().unwrap();
write!( f, "Circuit breaker [state : {:?}, failures : {}]", state.current_state, state.failure_count )
}
}
}
#[ cfg( feature = "circuit_breaker" ) ]
crate ::mod_interface!
{
exposed use private::CircuitBreakerState;
exposed use private::CircuitBreakerConfig;
exposed use private::CircuitBreaker;
}