gitdataai/libs/transport/circuit_breaker.rs
ZhenYi 14f6e1e500 feat(core): initialize project with access control and AI integration
- Add gitignore and prettier configuration files for project scaffolding
- Implement room access control service with project member verification
- Create user access key management with CRUD operations and activity logging
- Add accordion UI component for frontend expandable sections
- Implement room AI configuration with list, upsert, and delete operations
- Add AI event types for agent join/leave/status change tracking
- Create streaming AI processing services for mode and react patterns
- Build room AI service with model detection and idempotency handling
- Integrate chat service orchestration for AI message processing
- Add typing indicators and stream cancellation for AI interactions
- Implement mention parsing and context extraction for AI agents
2026-05-03 06:04:31 +08:00

141 lines
3.8 KiB
Rust

use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::Mutex;
#[derive(Clone)]
pub struct CircuitBreaker {
state: Arc<Mutex<CircuitState>>,
config: CircuitConfig,
}
#[derive(Clone)]
struct CircuitConfig {
failure_threshold: u32,
success_threshold: u32,
timeout: Duration,
half_open_max_calls: u32,
}
struct CircuitState {
status: CircuitStatus,
failure_count: u32,
success_count: u32,
last_failure_time: Option<Instant>,
half_open_calls: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CircuitStatus {
Closed,
Open,
HalfOpen,
}
impl CircuitBreaker {
pub fn new() -> Self {
Self {
state: Arc::new(Mutex::new(CircuitState {
status: CircuitStatus::Closed,
failure_count: 0,
success_count: 0,
last_failure_time: None,
half_open_calls: 0,
})),
config: CircuitConfig {
failure_threshold: 5,
success_threshold: 2,
timeout: Duration::from_secs(60),
half_open_max_calls: 3,
},
}
}
pub async fn call<F, T, E>(&self, f: F) -> Result<T, CircuitBreakerError<E>>
where
F: std::future::Future<Output = Result<T, E>>,
{
let mut state = self.state.lock().await;
match state.status {
CircuitStatus::Open => {
if let Some(last_failure) = state.last_failure_time {
if last_failure.elapsed() > self.config.timeout {
state.status = CircuitStatus::HalfOpen;
state.half_open_calls = 0;
} else {
return Err(CircuitBreakerError::Open);
}
}
}
CircuitStatus::HalfOpen => {
if state.half_open_calls >= self.config.half_open_max_calls {
return Err(CircuitBreakerError::Open);
}
state.half_open_calls += 1;
}
CircuitStatus::Closed => {}
}
drop(state);
match f.await {
Ok(result) => {
self.on_success().await;
Ok(result)
}
Err(e) => {
self.on_failure().await;
Err(CircuitBreakerError::Inner(e))
}
}
}
async fn on_success(&self) {
let mut state = self.state.lock().await;
state.failure_count = 0;
if state.status == CircuitStatus::HalfOpen {
state.success_count += 1;
if state.success_count >= self.config.success_threshold {
state.status = CircuitStatus::Closed;
state.success_count = 0;
}
}
}
async fn on_failure(&self) {
let mut state = self.state.lock().await;
state.failure_count += 1;
state.last_failure_time = Some(Instant::now());
if state.status == CircuitStatus::HalfOpen {
state.status = CircuitStatus::Open;
state.success_count = 0;
} else if state.failure_count >= self.config.failure_threshold {
state.status = CircuitStatus::Open;
}
}
pub async fn is_open(&self) -> bool {
let state = self.state.lock().await;
state.status == CircuitStatus::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, "{}", e),
}
}
}
impl<E: std::error::Error> std::error::Error for CircuitBreakerError<E> {}