use std::time::Duration;
pub struct HealthMonitor {
pub srtt: Duration,
pub loss_ratio: f64,
pub consecutive_failures: u32,
pub block_threshold: u32,
}
impl HealthMonitor {
pub fn new(block_threshold: u32) -> Self {
Self {
srtt: Duration::from_millis(50),
loss_ratio: 0.0,
consecutive_failures: 0,
block_threshold,
}
}
pub fn record_success(&mut self, rtt: Duration) {
self.consecutive_failures = 0;
self.srtt = Duration::from_micros(
(self.srtt.as_micros() as f64 * 0.875 + rtt.as_micros() as f64 * 0.125) as u64,
);
}
pub fn record_failure(&mut self) {
self.consecutive_failures += 1;
}
pub fn is_blocked(&self) -> bool {
self.consecutive_failures >= self.block_threshold
}
pub fn score(&self) -> f64 {
if self.is_blocked() {
return 0.0;
}
(1.0 - self.loss_ratio) * (1.0 / (1.0 + self.srtt.as_millis() as f64 / 1000.0))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn blocked_after_threshold() {
let mut h = HealthMonitor::new(3);
h.record_failure();
h.record_failure();
assert!(!h.is_blocked());
h.record_failure();
assert!(h.is_blocked());
assert_eq!(h.score(), 0.0);
}
#[test]
fn success_resets_failures() {
let mut h = HealthMonitor::new(2);
h.record_failure();
h.record_success(Duration::from_millis(10));
h.record_failure();
assert!(!h.is_blocked());
}
}