use std::time::Instant;
use tracing::info;
pub(super) const MAX_CONSECUTIVE_PANICS: u32 = 3;
pub(super) const PANIC_WINDOW_SECS: u64 = 60;
const DEGRADED_COOLDOWN_SECS: u64 = 30;
pub(super) struct CoreHealthWatchdog {
pub(super) consecutive_panics: u32,
window_start: Option<Instant>,
degraded: bool,
degraded_at: Option<Instant>,
}
impl CoreHealthWatchdog {
pub(super) fn new() -> Self {
Self {
consecutive_panics: 0,
window_start: None,
degraded: false,
degraded_at: None,
}
}
pub(super) fn record_panic(&mut self) -> bool {
let now = Instant::now();
if let Some(start) = self.window_start {
if now.duration_since(start).as_secs() > PANIC_WINDOW_SECS {
self.consecutive_panics = 0;
self.window_start = Some(now);
}
} else {
self.window_start = Some(now);
}
self.consecutive_panics += 1;
if self.consecutive_panics >= MAX_CONSECUTIVE_PANICS {
self.degraded = true;
self.degraded_at = Some(Instant::now());
}
self.degraded
}
pub(super) fn record_success(&mut self) {
if self.consecutive_panics > 0 {
self.consecutive_panics = 0;
self.window_start = None;
}
}
pub(super) fn is_degraded(&mut self) -> bool {
if self.degraded
&& let Some(degraded_at) = self.degraded_at
&& degraded_at.elapsed().as_secs() >= DEGRADED_COOLDOWN_SECS
{
info!(
cooldown_secs = DEGRADED_COOLDOWN_SECS,
"core recovered from degraded mode after cool-down"
);
self.degraded = false;
self.degraded_at = None;
self.consecutive_panics = 0;
self.window_start = None;
return false;
}
self.degraded
}
}