use crate::{Event, EventKind};
use std::collections::HashMap;
use std::sync::RwLock;
use std::time::Instant;
pub trait StorageBackend: Send + Sync + std::fmt::Debug {
fn record_success(&self, circuit_name: &str, duration: f64);
fn record_failure(&self, circuit_name: &str, duration: f64);
fn success_count(&self, circuit_name: &str, window_seconds: f64) -> usize;
fn failure_count(&self, circuit_name: &str, window_seconds: f64) -> usize;
fn clear(&self, circuit_name: &str);
fn clear_all(&self);
fn event_log(&self, circuit_name: &str, limit: usize) -> Vec<Event>;
fn monotonic_time(&self) -> f64;
}
#[derive(Debug)]
pub struct MemoryStorage {
events: RwLock<HashMap<String, Vec<Event>>>,
max_events: usize,
start_time: Instant,
}
impl MemoryStorage {
pub fn new() -> Self {
Self::with_max_events(1000)
}
pub fn with_max_events(max_events: usize) -> Self {
Self {
events: RwLock::new(HashMap::new()),
max_events,
start_time: Instant::now(),
}
}
fn record_event(&self, circuit_name: &str, kind: EventKind, duration: f64) {
let mut events = self.events.write().unwrap();
let circuit_events = events.entry(circuit_name.to_string()).or_default();
circuit_events.push(Event {
kind,
timestamp: self.monotonic_time(),
duration,
});
if circuit_events.len() > self.max_events {
let remove_count = (self.max_events / 10).max(1);
circuit_events.drain(0..remove_count);
}
}
fn count_events(&self, circuit_name: &str, kind: EventKind, window_seconds: f64) -> usize {
let events = self.events.read().unwrap();
let cutoff = self.monotonic_time() - window_seconds;
events
.get(circuit_name)
.map(|ev| {
ev.iter()
.filter(|e| e.kind == kind && e.timestamp >= cutoff)
.count()
})
.unwrap_or(0)
}
}
impl Default for MemoryStorage {
fn default() -> Self {
Self::new()
}
}
impl StorageBackend for MemoryStorage {
fn record_success(&self, circuit_name: &str, duration: f64) {
self.record_event(circuit_name, EventKind::Success, duration);
}
fn record_failure(&self, circuit_name: &str, duration: f64) {
self.record_event(circuit_name, EventKind::Failure, duration);
}
fn success_count(&self, circuit_name: &str, window_seconds: f64) -> usize {
self.count_events(circuit_name, EventKind::Success, window_seconds)
}
fn failure_count(&self, circuit_name: &str, window_seconds: f64) -> usize {
self.count_events(circuit_name, EventKind::Failure, window_seconds)
}
fn clear(&self, circuit_name: &str) {
let mut events = self.events.write().unwrap();
events.remove(circuit_name);
}
fn clear_all(&self) {
let mut events = self.events.write().unwrap();
events.clear();
}
fn event_log(&self, circuit_name: &str, limit: usize) -> Vec<Event> {
let events = self.events.read().unwrap();
events
.get(circuit_name)
.map(|ev| {
let start = if ev.len() > limit {
ev.len() - limit
} else {
0
};
ev[start..].to_vec()
})
.unwrap_or_default()
}
fn monotonic_time(&self) -> f64 {
self.start_time.elapsed().as_secs_f64()
}
}
#[derive(Debug, Clone, Copy)]
pub struct NullStorage {
start_time: Instant,
}
impl NullStorage {
pub fn new() -> Self {
Self {
start_time: Instant::now(),
}
}
}
impl Default for NullStorage {
fn default() -> Self {
Self::new()
}
}
impl StorageBackend for NullStorage {
fn record_success(&self, _circuit_name: &str, _duration: f64) {
}
fn record_failure(&self, _circuit_name: &str, _duration: f64) {
}
fn success_count(&self, _circuit_name: &str, _window_seconds: f64) -> usize {
0
}
fn failure_count(&self, _circuit_name: &str, _window_seconds: f64) -> usize {
0
}
fn clear(&self, _circuit_name: &str) {
}
fn clear_all(&self) {
}
fn event_log(&self, _circuit_name: &str, _limit: usize) -> Vec<Event> {
Vec::new()
}
fn monotonic_time(&self) -> f64 {
self.start_time.elapsed().as_secs_f64()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_storage_record_and_count() {
let storage = MemoryStorage::new();
storage.record_success("test_circuit", 0.1);
storage.record_success("test_circuit", 0.2);
storage.record_failure("test_circuit", 0.5);
assert_eq!(storage.success_count("test_circuit", 60.0), 2);
assert_eq!(storage.failure_count("test_circuit", 60.0), 1);
}
#[test]
fn test_memory_storage_clear() {
let storage = MemoryStorage::new();
storage.record_success("test_circuit", 0.1);
assert_eq!(storage.success_count("test_circuit", 60.0), 1);
storage.clear("test_circuit");
assert_eq!(storage.success_count("test_circuit", 60.0), 0);
}
#[test]
fn test_memory_storage_event_log() {
let storage = MemoryStorage::new();
storage.record_success("test_circuit", 0.1);
storage.record_failure("test_circuit", 0.2);
storage.record_success("test_circuit", 0.3);
let log = storage.event_log("test_circuit", 10);
assert_eq!(log.len(), 3);
assert_eq!(log[0].kind, EventKind::Success);
assert_eq!(log[1].kind, EventKind::Failure);
assert_eq!(log[2].kind, EventKind::Success);
}
#[test]
fn test_memory_storage_max_events_cleanup() {
let storage = MemoryStorage::with_max_events(100);
for i in 0..150 {
storage.record_success("test_circuit", i as f64 * 0.01);
}
let events = storage.events.read().unwrap();
let circuit_events = events.get("test_circuit").unwrap();
assert!(circuit_events.len() <= 100);
}
#[test]
fn test_memory_storage_small_max_events() {
let storage = MemoryStorage::with_max_events(5);
for i in 0..20 {
storage.record_success("test_circuit", i as f64 * 0.01);
}
let events = storage.events.read().unwrap();
let circuit_events = events.get("test_circuit").unwrap();
assert!(
circuit_events.len() <= 5,
"Expected <= 5 events, got {}",
circuit_events.len()
);
}
#[test]
fn test_memory_storage_monotonic_time() {
let storage = MemoryStorage::new();
storage.record_success("test_circuit", 0.1);
let time1 = storage.monotonic_time();
std::thread::sleep(std::time::Duration::from_millis(10));
storage.record_success("test_circuit", 0.2);
let time2 = storage.monotonic_time();
assert!(time2 > time1);
assert_eq!(storage.success_count("test_circuit", 1.0), 2);
}
#[test]
fn test_null_storage_discards_events() {
let storage = NullStorage::new();
storage.record_success("test_circuit", 0.1);
storage.record_failure("test_circuit", 0.2);
assert_eq!(storage.success_count("test_circuit", 60.0), 0);
assert_eq!(storage.failure_count("test_circuit", 60.0), 0);
}
#[test]
fn test_null_storage_empty_event_log() {
let storage = NullStorage::new();
storage.record_success("test_circuit", 0.1);
storage.record_failure("test_circuit", 0.2);
let log = storage.event_log("test_circuit", 10);
assert_eq!(log.len(), 0);
}
#[test]
fn test_null_storage_clear_operations() {
let storage = NullStorage::new();
storage.clear("test_circuit");
storage.clear_all();
assert_eq!(storage.success_count("test_circuit", 60.0), 0);
}
#[test]
fn test_null_storage_monotonic_time() {
let storage = NullStorage::new();
let time1 = storage.monotonic_time();
std::thread::sleep(std::time::Duration::from_millis(10));
let time2 = storage.monotonic_time();
assert!(time2 > time1);
}
#[test]
fn test_null_storage_with_circuit_breaker() {
use std::sync::Arc;
let storage = Arc::new(NullStorage::new());
let mut circuit = crate::CircuitBreaker::builder("test")
.storage(storage)
.failure_threshold(3)
.build();
let _ = circuit.call(|| Err::<(), _>("error 1"));
let _ = circuit.call(|| Err::<(), _>("error 2"));
let _ = circuit.call(|| Err::<(), _>("error 3"));
assert!(circuit.is_closed());
assert!(!circuit.is_open());
}
}