mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-11-02 09:52:20 +00:00
- Add module-level documentation to lib.rs with usage example - Document Claims and AuthConfig structs with examples in auth.rs - Add ChargingEngine struct documentation with initialization example - Document ChargingRule, SubscriberAccount, AccountStatus, UsageEvent, UsageType, and RatingPlan with examples in types.rs - Add CircuitState, CircuitBreakerError, and CircuitBreaker documentation with examples in circuit_breaker.rs
180 lines
4.9 KiB
Rust
180 lines
4.9 KiB
Rust
use std::sync::Arc;
|
|
use std::time::{Duration, Instant};
|
|
use tokio::sync::RwLock;
|
|
|
|
/// Circuit breaker state
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use charging_engine::circuit_breaker::CircuitState;
|
|
///
|
|
/// let state = CircuitState::Closed;
|
|
/// assert_eq!(state, CircuitState::Closed);
|
|
/// ```
|
|
#[derive(Debug, Clone, PartialEq, Copy)]
|
|
pub enum CircuitState {
|
|
Closed,
|
|
Open,
|
|
HalfOpen,
|
|
}
|
|
|
|
/// Circuit breaker error type
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use charging_engine::circuit_breaker::CircuitBreakerError;
|
|
///
|
|
/// let error: CircuitBreakerError<&str> = CircuitBreakerError::Open;
|
|
/// ```
|
|
#[derive(Debug)]
|
|
pub enum CircuitBreakerError<E> {
|
|
Open,
|
|
Inner(E),
|
|
}
|
|
|
|
impl<E: std::fmt::Display> std::fmt::Display for CircuitBreakerError<E> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
CircuitBreakerError::Open => write!(f, "Circuit breaker is open"),
|
|
CircuitBreakerError::Inner(e) => write!(f, "Wrapped error: {}", e),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<E: std::error::Error + Send + Sync + 'static> std::error::Error for CircuitBreakerError<E> {}
|
|
|
|
/// Circuit breaker for fault tolerance
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust,no_run
|
|
/// use charging_engine::circuit_breaker::CircuitBreaker;
|
|
/// use std::time::Duration;
|
|
///
|
|
/// let breaker = CircuitBreaker::new(5, Duration::from_secs(60));
|
|
/// ```
|
|
#[derive(Clone)]
|
|
pub struct CircuitBreaker {
|
|
state: Arc<RwLock<CircuitState>>,
|
|
failure_count: Arc<RwLock<u32>>,
|
|
failure_threshold: u32,
|
|
timeout: Duration,
|
|
last_failure_time: Arc<RwLock<Option<Instant>>>,
|
|
}
|
|
|
|
impl CircuitBreaker {
|
|
/// Create a new circuit breaker
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `failure_threshold` - Number of failures before opening the circuit
|
|
/// * `timeout` - Duration to wait before attempting to close the circuit
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use charging_engine::circuit_breaker::CircuitBreaker;
|
|
/// use std::time::Duration;
|
|
///
|
|
/// let breaker = CircuitBreaker::new(5, Duration::from_secs(60));
|
|
/// ```
|
|
pub fn new(failure_threshold: u32, timeout: Duration) -> Self {
|
|
Self {
|
|
state: Arc::new(RwLock::new(CircuitState::Closed)),
|
|
failure_count: Arc::new(RwLock::new(0)),
|
|
failure_threshold,
|
|
timeout,
|
|
last_failure_time: Arc::new(RwLock::new(None)),
|
|
}
|
|
}
|
|
|
|
pub async fn execute<F, Fut, T, E>(&self, operation: F) -> Result<T, CircuitBreakerError<E>>
|
|
where
|
|
F: FnOnce() -> Fut,
|
|
Fut: std::future::Future<Output = Result<T, E>>,
|
|
E: std::error::Error + Send + Sync + 'static,
|
|
{
|
|
// Check if circuit is open and should attempt to close
|
|
self.check_state().await;
|
|
|
|
let state = *self.state.read().await;
|
|
|
|
if state == CircuitState::Open {
|
|
return Err(CircuitBreakerError::Open);
|
|
}
|
|
|
|
// Execute the operation
|
|
let result = operation().await;
|
|
|
|
match result {
|
|
Ok(value) => {
|
|
self.on_success().await;
|
|
Ok(value)
|
|
}
|
|
Err(error) => {
|
|
self.on_failure().await;
|
|
Err(CircuitBreakerError::Inner(error))
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn check_state(&self) {
|
|
let state = *self.state.read().await;
|
|
let last_failure = *self.last_failure_time.read().await;
|
|
|
|
if state == CircuitState::Open {
|
|
if let Some(failure_time) = last_failure {
|
|
if failure_time.elapsed() > self.timeout {
|
|
// Transition to half-open
|
|
*self.state.write().await = CircuitState::HalfOpen;
|
|
*self.failure_count.write().await = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn on_success(&self) {
|
|
let state = *self.state.read().await;
|
|
|
|
match state {
|
|
CircuitState::HalfOpen => {
|
|
// Reset to closed on success in half-open
|
|
*self.state.write().await = CircuitState::Closed;
|
|
*self.failure_count.write().await = 0;
|
|
}
|
|
CircuitState::Closed => {
|
|
*self.failure_count.write().await = 0;
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
async fn on_failure(&self) {
|
|
let mut failure_count = self.failure_count.write().await;
|
|
*failure_count += 1;
|
|
|
|
if *failure_count >= self.failure_threshold {
|
|
*self.state.write().await = CircuitState::Open;
|
|
*self.last_failure_time.write().await = Some(Instant::now());
|
|
}
|
|
}
|
|
|
|
pub async fn state(&self) -> CircuitState {
|
|
*self.state.read().await
|
|
}
|
|
|
|
pub async fn reset(&self) {
|
|
*self.state.write().await = CircuitState::Closed;
|
|
*self.failure_count.write().await = 0;
|
|
*self.last_failure_time.write().await = None;
|
|
}
|
|
}
|
|
|
|
impl Default for CircuitBreaker {
|
|
fn default() -> Self {
|
|
Self::new(5, Duration::from_secs(60))
|
|
}
|
|
}
|