use std::sync::Arc;
use std::time::Instant; use core::time::Duration;
use tokio::sync::RwLock;
#[ derive( Debug, Clone ) ]
pub struct CircuitBreakerConfig
{
pub failure_threshold : u32,
pub success_threshold : u32,
pub timeout : Duration,
}
impl Default for CircuitBreakerConfig
{
#[ inline ]
fn default() -> Self
{
Self {
failure_threshold : 5,
success_threshold : 2,
timeout : Duration::from_secs( 60 ),
}
}
}
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
pub enum CircuitState
{
Closed,
Open,
HalfOpen,
}
#[ derive( Debug ) ]
struct CircuitBreakerState
{
state : CircuitState,
failure_count : u32,
success_count : u32,
last_failure_time : Option< Instant >,
}
#[ derive( Debug, Clone ) ]
pub struct CircuitBreaker
{
config : CircuitBreakerConfig,
state : Arc< RwLock< CircuitBreakerState > >,
}
impl CircuitBreaker
{
#[ inline ]
#[ must_use ]
pub fn new( config : CircuitBreakerConfig ) -> Self
{
Self {
config,
state : Arc::new( RwLock::new( CircuitBreakerState {
state : CircuitState::Closed,
failure_count : 0,
success_count : 0,
last_failure_time : None,
} )),
}
}
#[ inline ]
pub async fn execute< F, T, E >( &self, f : F ) -> Result< T, CircuitBreakerError< E > >
where
F: core::future::Future< Output = Result< T, E > >,
{
{
let mut state = self.state.write( ).await;
match state.state
{
CircuitState::Open => {
if let Some( last_failure ) = state.last_failure_time
{
if last_failure.elapsed( ) >= self.config.timeout
{
state.state = CircuitState::HalfOpen;
state.success_count = 0;
state.failure_count = 0;
} else {
return Err( CircuitBreakerError::CircuitOpen );
}
}
}
CircuitState::Closed | CircuitState::HalfOpen => {
}
}
}
let result = f.await;
{
let mut state = self.state.write( ).await;
match result
{
Ok( ref _value ) => {
match state.state
{
CircuitState::Closed => {
state.failure_count = 0;
}
CircuitState::HalfOpen => {
state.success_count += 1;
if state.success_count >= self.config.success_threshold
{
state.state = CircuitState::Closed;
state.failure_count = 0;
state.success_count = 0;
}
}
CircuitState::Open => {
state.state = CircuitState::HalfOpen;
state.success_count = 1;
state.failure_count = 0;
}
}
}
Err( ref _e ) => {
state.failure_count += 1;
state.last_failure_time = Some( Instant::now( ));
match state.state
{
CircuitState::Closed => {
if state.failure_count >= self.config.failure_threshold
{
state.state = CircuitState::Open;
}
}
CircuitState::HalfOpen => {
state.state = CircuitState::Open;
state.success_count = 0;
}
CircuitState::Open => {
}
}
}
}
}
result.map_err( CircuitBreakerError::Operation )
}
#[ inline ]
pub async fn state( &self ) -> CircuitState
{
self.state.read( ).await.state
}
#[ inline ]
pub async fn is_open( &self ) -> bool
{
self.state.read( ).await.state == CircuitState::Open
}
#[ inline ]
pub async fn is_closed( &self ) -> bool
{
self.state.read( ).await.state == CircuitState::Closed
}
#[ inline ]
pub async fn is_half_open( &self ) -> bool
{
self.state.read( ).await.state == CircuitState::HalfOpen
}
#[ inline ]
pub async fn failure_count( &self ) -> u32
{
self.state.read( ).await.failure_count
}
#[ inline ]
pub async fn success_count( &self ) -> u32
{
self.state.read( ).await.success_count
}
#[ inline ]
pub async fn reset( &self )
{
let mut state = self.state.write( ).await;
state.state = CircuitState::Closed;
state.failure_count = 0;
state.success_count = 0;
state.last_failure_time = None;
}
}
#[ derive( Debug ) ]
pub enum CircuitBreakerError< E >
{
CircuitOpen,
Operation( E ),
}
impl< E > core::fmt::Display for CircuitBreakerError< E >
where
E: core::fmt::Display,
{
#[ inline ]
fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result
{
match self
{
Self::CircuitOpen => write!( f, "Circuit breaker is open" ),
Self::Operation( e ) => write!( f, "Operation failed : {e}" ),
}
}
}
impl< E > std::error::Error for CircuitBreakerError< E >
where
E: std::error::Error + 'static,
{
#[ inline ]
fn source( &self ) -> Option< &( dyn std::error::Error + 'static ) >
{
match self
{
Self::CircuitOpen => None,
Self::Operation( e ) => Some( e ),
}
}
}