use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct PoolHealthReport {
pub check_duration: Duration,
pub total_check_time: Duration,
pub pool_size: u32,
pub idle_connections: u32,
pub active_connections: u32,
pub total_acquires: u64,
pub total_errors: u64,
pub error_rate: f64,
pub created_at: Instant,
}
impl PoolHealthReport {
pub fn is_healthy(&self) -> bool {
self.is_responsive() && self.has_acceptable_error_rate() && self.has_available_connections()
}
pub fn is_responsive(&self) -> bool {
self.check_duration < Duration::from_millis(1000) }
pub fn has_acceptable_error_rate(&self) -> bool {
self.error_rate < 5.0 }
pub fn has_available_connections(&self) -> bool {
self.idle_connections > 0 || self.active_connections < self.pool_size
}
pub fn utilization(&self) -> f64 {
if self.pool_size > 0 {
(self.active_connections as f64 / self.pool_size as f64) * 100.0
} else {
0.0
}
}
pub fn success_rate(&self) -> f64 {
100.0 - self.error_rate
}
pub fn uptime(&self) -> Duration {
self.created_at.elapsed()
}
pub fn status_summary(&self) -> HealthStatus {
if !self.is_responsive() {
HealthStatus::Unhealthy {
reason: "Pool is not responsive".to_string(),
}
} else if !self.has_acceptable_error_rate() {
HealthStatus::Degraded {
reason: format!("High error rate: {:.1}%", self.error_rate),
severity: if self.error_rate > 25.0 {
Severity::High
} else {
Severity::Medium
},
}
} else if !self.has_available_connections() {
HealthStatus::Degraded {
reason: "Pool is exhausted".to_string(),
severity: Severity::High,
}
} else if self.utilization() > 80.0 {
HealthStatus::Warning {
reason: format!("High utilization: {:.1}%", self.utilization()),
}
} else {
HealthStatus::Healthy
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum HealthStatus {
Healthy,
Warning { reason: String },
Degraded { reason: String, severity: Severity },
Unhealthy { reason: String },
}
#[derive(Debug, Clone, PartialEq)]
pub enum Severity {
Low,
Medium,
High,
}
impl std::fmt::Display for HealthStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
HealthStatus::Healthy => write!(f, "HEALTHY"),
HealthStatus::Warning { reason } => write!(f, "WARNING: {}", reason),
HealthStatus::Degraded { reason, severity } => {
write!(f, "DEGRADED ({:?}): {}", severity, reason)
}
HealthStatus::Unhealthy { reason } => write!(f, "UNHEALTHY: {}", reason),
}
}
}
#[derive(Debug, Clone)]
pub struct HealthMonitorConfig {
pub max_response_time: Duration,
pub max_error_rate: f64,
pub warning_utilization_threshold: f64,
pub check_interval: Duration,
pub detailed_reports: bool,
}
impl Default for HealthMonitorConfig {
fn default() -> Self {
Self {
max_response_time: Duration::from_millis(1000),
max_error_rate: 5.0,
warning_utilization_threshold: 80.0,
check_interval: Duration::from_secs(30),
detailed_reports: true,
}
}
}
pub struct HealthMonitor {
config: HealthMonitorConfig,
last_check: Option<Instant>,
history: Vec<PoolHealthReport>,
max_history: usize,
}
impl HealthMonitor {
pub fn new(config: HealthMonitorConfig) -> Self {
Self {
config,
last_check: None,
history: Vec::new(),
max_history: 100, }
}
pub fn record_health_report(&mut self, report: PoolHealthReport) {
self.last_check = Some(Instant::now());
self.history.push(report);
if self.history.len() > self.max_history {
self.history.remove(0);
}
}
pub fn latest_report(&self) -> Option<&PoolHealthReport> {
self.history.last()
}
pub fn health_trend(&self, count: usize) -> HealthTrend {
let recent_reports = self.history.iter().rev().take(count).collect::<Vec<_>>();
if recent_reports.is_empty() {
return HealthTrend::Unknown;
}
let avg_error_rate =
recent_reports.iter().map(|r| r.error_rate).sum::<f64>() / recent_reports.len() as f64;
let avg_utilization = recent_reports.iter().map(|r| r.utilization()).sum::<f64>()
/ recent_reports.len() as f64;
if avg_error_rate > self.config.max_error_rate {
HealthTrend::Degrading
} else if avg_utilization > self.config.warning_utilization_threshold {
HealthTrend::Warning
} else {
HealthTrend::Stable
}
}
pub fn should_check_health(&self) -> bool {
match self.last_check {
Some(last) => last.elapsed() >= self.config.check_interval,
None => true,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum HealthTrend {
Unknown,
Improving,
Stable,
Warning,
Degrading,
}