use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ShutdownReason {
Graceful,
Signal {
signal: i32,
},
Crash {
message: String,
},
Coma {
downtime_seconds: u64,
},
}
impl ShutdownReason {
pub fn intensity(&self) -> f32 {
match self {
ShutdownReason::Graceful => 0.1, ShutdownReason::Signal { .. } => 0.4, ShutdownReason::Crash { .. } => 0.8, ShutdownReason::Coma { .. } => 0.9, }
}
pub fn lineage_key(&self) -> &'static str {
match self {
ShutdownReason::Graceful => super::lineages::SHUTDOWN_GRACEFUL,
ShutdownReason::Signal { .. } => super::lineages::SHUTDOWN_FORCED,
ShutdownReason::Crash { .. } => super::lineages::SHOCK,
ShutdownReason::Coma { .. } => super::lineages::COMA,
}
}
pub fn description(&self) -> String {
match self {
ShutdownReason::Graceful => "graceful shutdown".into(),
ShutdownReason::Signal { signal } => format!("forced (signal {})", signal),
ShutdownReason::Crash { message } => format!("crash: {}", message),
ShutdownReason::Coma { downtime_seconds } => {
format!("coma: {}s downtime", downtime_seconds)
}
}
}
}
#[derive(Default)]
pub struct ShutdownTracker {
pub last_reason: Option<ShutdownReason>,
pub last_timestamp: Option<u64>,
}
impl ShutdownTracker {
pub fn record(&mut self, reason: ShutdownReason, timestamp: u64) {
self.last_reason = Some(reason);
self.last_timestamp = Some(timestamp);
}
pub fn was_traumatic(&self) -> bool {
self.last_reason
.as_ref()
.map(|r| r.intensity() > 0.5)
.unwrap_or(false)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_shutdown_intensity() {
assert!(ShutdownReason::Graceful.intensity() < 0.2);
assert!(
ShutdownReason::Crash {
message: "test".into()
}
.intensity()
> 0.7
);
}
#[test]
fn test_traumatic_detection() {
let mut tracker = ShutdownTracker::default();
tracker.record(ShutdownReason::Graceful, 1000);
assert!(!tracker.was_traumatic());
tracker.record(
ShutdownReason::Crash {
message: "panic".into(),
},
2000,
);
assert!(tracker.was_traumatic());
}
}