use std::time::{Duration, Instant};
pub struct Heartbeat {
interval: Duration,
last_sent: Instant,
last_recv: Instant,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HeartbeatState {
Healthy,
Suspect,
Timeout,
}
impl Heartbeat {
pub fn new(interval: Duration, now: Instant) -> Self {
Self {
interval,
last_sent: now,
last_recv: now,
}
}
pub fn mark_sent(&mut self, now: Instant) {
self.last_sent = now;
}
pub fn mark_recv(&mut self, now: Instant) {
self.last_recv = now;
}
pub fn should_send(&self, now: Instant) -> bool {
now.duration_since(self.last_sent) >= self.interval
}
pub fn state(&self, now: Instant) -> HeartbeatState {
let since = now.duration_since(self.last_recv);
if since >= self.interval * 5 {
HeartbeatState::Timeout
} else if since >= self.interval * 3 {
HeartbeatState::Suspect
} else {
HeartbeatState::Healthy
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn heartbeat_state_progression() {
let start = Instant::now();
let hb = Heartbeat::new(Duration::from_secs(5), start);
assert_eq!(hb.state(start), HeartbeatState::Healthy);
let t = start + Duration::from_secs(16); assert_eq!(hb.state(t), HeartbeatState::Suspect);
let t = start + Duration::from_secs(26); assert_eq!(hb.state(t), HeartbeatState::Timeout);
}
}