use std::time::{Instant, Duration};
use tokio::sync::RwLock;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct HealthStatus {
pub is_healthy: bool,
pub last_check: Instant,
pub last_error: Option<String>,
pub consecutive_failures: u32,
pub component_status: Vec<ComponentStatus>,
}
impl HealthStatus {
pub fn new() -> Self {
Self {
is_healthy: false,
last_check: Instant::now(),
last_error: None,
consecutive_failures: 0,
component_status: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct ComponentStatus {
pub name: String,
pub is_healthy: bool,
pub response_time: Duration,
pub last_error: Option<String>,
}
pub struct HealthChecker {
check_interval: Duration,
last_check: Arc<RwLock<Option<Instant>>>,
health_status: Arc<RwLock<HealthStatus>>,
consecutive_failures: Arc<std::sync::atomic::AtomicU32>,
}
impl HealthChecker {
pub fn new(check_interval: Duration) -> Self {
Self {
check_interval,
last_check: Arc::new(RwLock::new(None)),
health_status: Arc::new(RwLock::new(HealthStatus::new())), consecutive_failures: Arc::new(std::sync::atomic::AtomicU32::new(0)),
}
}
pub async fn check_health(&self) -> Result<HealthStatus, String> {
let mut status = HealthStatus::new();
status.last_check = Instant::now();
let components = self.check_components().await;
status.component_status = components;
status.is_healthy = status.component_status.iter()
.all(|component| component.is_healthy);
if !status.is_healthy {
status.consecutive_failures = self.consecutive_failures.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + 1;
status.last_error = Some("One or more components are unhealthy".to_string());
} else {
self.consecutive_failures.store(0, std::sync::atomic::Ordering::Relaxed);
status.consecutive_failures = 0;
status.last_error = None;
}
{
let mut health_status = self.health_status.write().await;
*health_status = status.clone();
}
{
let mut last_check = self.last_check.write().await;
*last_check = Some(Instant::now());
}
Ok(status)
}
async fn check_components(&self) -> Vec<ComponentStatus> {
let mut components = Vec::new();
components.push(ComponentStatus {
name: "database".to_string(),
is_healthy: true,
response_time: Duration::from_millis(10),
last_error: None,
});
components.push(ComponentStatus {
name: "cache".to_string(),
is_healthy: true,
response_time: Duration::from_millis(5),
last_error: None,
});
components.push(ComponentStatus {
name: "replicas".to_string(),
is_healthy: true,
response_time: Duration::from_millis(15),
last_error: None,
});
components
}
pub async fn start_periodic_checks(self: Arc<Self>) -> tokio::task::JoinHandle<()> {
let checker = self.clone();
let check_interval = self.check_interval;
tokio::spawn(async move {
let mut interval = tokio::time::interval(check_interval);
println!("[HealthChecker] Starting periodic checks with interval: {:?}", check_interval);
loop {
interval.tick().await;
match checker.check_health().await {
Ok(health) => {
if health.is_healthy {
checker.consecutive_failures.store(0, std::sync::atomic::Ordering::Relaxed);
println!("[HealthChecker] Health check passed - all systems operational");
} else {
let failures = checker.consecutive_failures.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + 1;
println!("[HealthChecker] Health check failed {} times consecutively", failures);
}
}
Err(e) => {
let failures = checker.consecutive_failures.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + 1;
println!("[HealthChecker] Health check error ({}): {}", failures, e);
}
}
}
})
}
pub async fn get_current_status(&self) -> HealthStatus {
let health_status = self.health_status.read().await;
health_status.clone()
}
pub fn get_check_interval(&self) -> Duration {
self.check_interval
}
pub fn set_check_interval(&mut self, interval: Duration) {
self.check_interval = interval;
println!("[HealthChecker] Check interval updated to: {:?}", interval);
}
pub async fn should_perform_check(&self) -> bool {
let last_check = self.last_check.read().await;
match *last_check {
Some(time) => Instant::now().duration_since(time) >= self.check_interval,
None => true, }
}
pub async fn force_check(&self) -> Result<HealthStatus, String> {
self.check_health().await
}
pub fn get_consecutive_failures(&self) -> u32 {
self.consecutive_failures.load(std::sync::atomic::Ordering::Relaxed)
}
}