use crate::reliability::{
CircuitBreakerConfig,
RateLimiterConfig,
FailoverConfig,
HealthCheckConfig,
};
use std::collections::VecDeque;
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::RwLock;
#[ derive( Debug, Clone ) ]
pub struct ReliabilityConfig
{
pub circuit_breaker : Option< CircuitBreakerConfig >,
pub rate_limiter : Option< RateLimiterConfig >,
pub failover : Option< FailoverConfig >,
pub health_check : Option< HealthCheckConfig >,
pub timestamp : Instant,
}
impl Default for ReliabilityConfig
{
#[ inline ]
fn default() -> Self
{
Self
{
circuit_breaker : None,
rate_limiter : None,
failover : None,
health_check : None,
timestamp : Instant::now( ),
}
}
}
impl ReliabilityConfig
{
#[ inline ]
#[ must_use ]
pub fn new() -> Self
{
Self::default( )
}
#[ inline ]
pub fn update_timestamp( &mut self )
{
self.timestamp = Instant::now( );
}
}
pub type ConfigWatcherCallback = Arc< dyn Fn( &ReliabilityConfig, &ReliabilityConfig ) + Send + Sync >;
struct DynamicConfigState
{
current : ReliabilityConfig,
watchers : Vec< ConfigWatcherCallback >,
history : VecDeque< ReliabilityConfig >,
max_history : usize,
}
#[ derive( Clone ) ]
pub struct DynamicConfig
{
state : Arc< RwLock< DynamicConfigState > >,
}
impl core::fmt::Debug for DynamicConfig
{
#[ inline ]
fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result
{
f.debug_struct( "DynamicConfig" )
.field( "state", &"< DynamicConfigState >" )
.finish( )
}
}
impl DynamicConfig
{
#[ inline ]
#[ must_use ]
pub fn new( config : ReliabilityConfig ) -> Self
{
Self::with_history_size( config, 10 )
}
#[ inline ]
#[ must_use ]
pub fn with_history_size( config : ReliabilityConfig, max_history : usize ) -> Self
{
Self
{
state : Arc::new( RwLock::new( DynamicConfigState
{
current : config,
watchers : Vec::new( ),
history : VecDeque::new( ),
max_history,
} )),
}
}
#[ inline ]
pub async fn get( &self ) -> ReliabilityConfig
{
let state = self.state.read( ).await;
state.current.clone( )
}
#[ inline ]
pub async fn update( &self, mut new_config : ReliabilityConfig ) -> Result< ( ), ConfigError >
{
Self::validate( &new_config )?;
new_config.update_timestamp( );
let mut state = self.state.write( ).await;
let old_config = state.current.clone( );
state.history.push_back( old_config.clone( ));
while state.history.len( ) > state.max_history
{
state.history.pop_front( );
}
state.current = new_config.clone( );
for watcher in &state.watchers
{
watcher( &old_config, &new_config );
}
Ok( ( ))
}
#[ inline ]
pub async fn add_watcher< F >( &self, watcher : F )
where
F : Fn( &ReliabilityConfig, &ReliabilityConfig ) + Send + Sync + 'static,
{
let mut state = self.state.write( ).await;
state.watchers.push( Arc::new( watcher ));
}
#[ inline ]
pub async fn clear_watchers( &self )
{
let mut state = self.state.write( ).await;
state.watchers.clear( );
}
#[ inline ]
pub async fn watcher_count( &self ) -> usize
{
let state = self.state.read( ).await;
state.watchers.len( )
}
#[ inline ]
pub async fn rollback( &self ) -> Result< ( ), ConfigError >
{
let mut state = self.state.write( ).await;
let old_config = state.history.pop_back( )
.ok_or( ConfigError::NoHistory )?;
let current_config = state.current.clone( );
state.current = old_config.clone( );
for watcher in &state.watchers
{
watcher( ¤t_config, &old_config );
}
Ok( ( ))
}
#[ inline ]
pub async fn get_history( &self ) -> Vec< ReliabilityConfig >
{
let state = self.state.read( ).await;
state.history.iter( ).cloned( ).collect( )
}
#[ inline ]
pub async fn history_size( &self ) -> usize
{
let state = self.state.read( ).await;
state.history.len( )
}
#[ inline ]
pub async fn clear_history( &self )
{
let mut state = self.state.write( ).await;
state.history.clear( );
}
#[ inline ]
fn validate( config : &ReliabilityConfig ) -> Result< ( ), ConfigError >
{
if let Some( ref cb_config ) = config.circuit_breaker
{
if cb_config.failure_threshold == 0
{
return Err( ConfigError::ValidationFailed {
field : "circuit_breaker.failure_threshold".to_string( ),
reason : "must be greater than 0".to_string( ),
} );
}
if cb_config.success_threshold == 0
{
return Err( ConfigError::ValidationFailed {
field : "circuit_breaker.success_threshold".to_string( ),
reason : "must be greater than 0".to_string( ),
} );
}
}
if let Some( ref rl_config ) = config.rate_limiter
{
if rl_config.requests_per_second.is_none( )
&& rl_config.requests_per_minute.is_none( )
&& rl_config.requests_per_hour.is_none( )
{
return Err( ConfigError::ValidationFailed {
field : "rate_limiter".to_string( ),
reason : "at least one rate limit must be set".to_string( ),
} );
}
}
if let Some( ref fo_config ) = config.failover
{
if fo_config.endpoints.is_empty( )
{
return Err( ConfigError::ValidationFailed {
field : "failover.endpoints".to_string( ),
reason : "must have at least one endpoint".to_string( ),
} );
}
if fo_config.failure_threshold == 0
{
return Err( ConfigError::ValidationFailed {
field : "failover.failure_threshold".to_string( ),
reason : "must be greater than 0".to_string( ),
} );
}
}
if let Some( ref hc_config ) = config.health_check
{
if hc_config.endpoint.is_empty( )
{
return Err( ConfigError::ValidationFailed {
field : "health_check.endpoint".to_string( ),
reason : "endpoint must not be empty".to_string( ),
} );
}
if hc_config.unhealthy_threshold == 0
{
return Err( ConfigError::ValidationFailed {
field : "health_check.unhealthy_threshold".to_string( ),
reason : "must be greater than 0".to_string( ),
} );
}
}
Ok( ( ))
}
#[ inline ]
#[ must_use ]
pub fn is_valid( config : &ReliabilityConfig ) -> bool
{
Self::validate( config ).is_ok( )
}
}
#[ derive( Debug ) ]
pub enum ConfigError
{
ValidationFailed
{
field : String,
reason : String,
},
NoHistory,
}
impl core::fmt::Display for ConfigError
{
#[ inline ]
fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result
{
match self
{
Self::ValidationFailed { field, reason } => {
write!( f, "Validation failed for {field}: {reason}" )
}
Self::NoHistory => write!( f, "No configuration history available for rollback" ),
}
}
}
impl std::error::Error for ConfigError {}