use std::sync::{Arc, Mutex};
use std::time::Instant;
#[async_trait::async_trait]
pub trait ServiceProvider: Send + Sync {
fn name(&self) -> &str;
fn is_healthy(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BreakerState {
Closed,
Open,
HalfOpen,
}
pub struct CircuitBreaker {
state: Mutex<BreakerState>,
failures: Mutex<usize>,
last_failure: Mutex<Option<Instant>>,
threshold: usize,
reset_timeout: std::time::Duration,
}
impl CircuitBreaker {
pub fn new(threshold: usize, reset_timeout: std::time::Duration) -> Self {
Self {
state: Mutex::new(BreakerState::Closed),
failures: Mutex::new(0),
last_failure: Mutex::new(None),
threshold,
reset_timeout,
}
}
pub fn threshold(&self) -> usize {
self.threshold
}
pub fn reset_timeout(&self) -> std::time::Duration {
self.reset_timeout
}
pub fn state(&self) -> BreakerState {
*self.state.lock().unwrap()
}
pub fn record_success(&self) {
*self.state.lock().unwrap() = BreakerState::Closed;
*self.failures.lock().unwrap() = 0;
}
pub fn record_failure(&self) {
let mut f = self.failures.lock().unwrap();
*f += 1;
*self.last_failure.lock().unwrap() = Some(Instant::now());
if *f >= self.threshold {
*self.state.lock().unwrap() = BreakerState::Open;
}
}
pub fn is_closed(&self) -> bool {
self.state() == BreakerState::Closed
}
}
struct ProviderNode {
provider: Arc<dyn ServiceProvider>,
breaker: CircuitBreaker,
}
pub struct ApplicationService {
name: String,
providers: Vec<ProviderNode>,
preferred_idx: Option<usize>,
}
impl ApplicationService {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
providers: vec![],
preferred_idx: None,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn register_provider<P: ServiceProvider + 'static>(&mut self, provider: P) {
let node = ProviderNode {
provider: Arc::new(provider),
breaker: CircuitBreaker::new(5, std::time::Duration::from_secs(60)),
};
if self.preferred_idx.is_none() {
self.preferred_idx = Some(0);
}
self.providers.push(node);
}
pub fn get_provider_by_name(&self, name: &str) -> Option<Arc<dyn ServiceProvider>> {
self.providers
.iter()
.find(|n| n.provider.name() == name)
.map(|n| n.provider.clone())
}
pub fn provider_names(&self) -> Vec<String> {
self.providers
.iter()
.map(|n| n.provider.name().to_string())
.collect()
}
pub fn available(&self) -> Option<Arc<dyn ServiceProvider>> {
for node in &self.providers {
if node.breaker.is_closed() {
return Some(node.provider.clone());
}
if node.provider.is_healthy() {
node.breaker.record_success();
return Some(node.provider.clone());
}
}
None
}
}