use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;
#[derive(Debug)]
pub struct ReadinessState {
pub command: String,
pub args: Vec<String>,
started_at: Instant,
ms_to_first_response: AtomicU64,
ms_to_first_nonempty: AtomicU64,
ms_to_last_response: AtomicU64,
response_count: AtomicU64,
nonempty_count: AtomicU64,
failure_count: AtomicU64,
}
impl ReadinessState {
pub(super) fn new(command: String, args: Vec<String>) -> Self {
Self {
command,
args,
started_at: Instant::now(),
ms_to_first_response: AtomicU64::new(0),
ms_to_first_nonempty: AtomicU64::new(0),
ms_to_last_response: AtomicU64::new(0),
response_count: AtomicU64::new(0),
nonempty_count: AtomicU64::new(0),
failure_count: AtomicU64::new(0),
}
}
pub(super) fn record_ok(&self, was_nonempty: bool) {
let elapsed = self.started_at.elapsed().as_millis() as u64;
let ms = elapsed.max(1);
let _ =
self.ms_to_first_response
.compare_exchange(0, ms, Ordering::Relaxed, Ordering::Relaxed);
if was_nonempty {
let _ = self.ms_to_first_nonempty.compare_exchange(
0,
ms,
Ordering::Relaxed,
Ordering::Relaxed,
);
self.nonempty_count.fetch_add(1, Ordering::Relaxed);
}
self.ms_to_last_response.store(ms, Ordering::Relaxed);
self.response_count.fetch_add(1, Ordering::Relaxed);
}
pub(super) fn record_failure(&self) {
self.failure_count.fetch_add(1, Ordering::Relaxed);
}
pub fn snapshot(&self) -> ReadinessSnapshot {
let read = |a: &AtomicU64| a.load(Ordering::Relaxed);
let opt = |v: u64| if v == 0 { None } else { Some(v) };
ReadinessSnapshot {
command: self.command.clone(),
args: self.args.clone(),
elapsed_ms: self.started_at.elapsed().as_millis() as u64,
ms_to_first_response: opt(read(&self.ms_to_first_response)),
ms_to_first_nonempty: opt(read(&self.ms_to_first_nonempty)),
ms_to_last_response: opt(read(&self.ms_to_last_response)),
response_count: read(&self.response_count),
nonempty_count: read(&self.nonempty_count),
failure_count: read(&self.failure_count),
}
}
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct ReadinessSnapshot {
pub command: String,
pub args: Vec<String>,
pub elapsed_ms: u64,
pub ms_to_first_response: Option<u64>,
pub ms_to_first_nonempty: Option<u64>,
pub ms_to_last_response: Option<u64>,
pub response_count: u64,
pub nonempty_count: u64,
pub failure_count: u64,
}
impl ReadinessSnapshot {
pub fn is_ready(&self) -> bool {
self.ms_to_first_nonempty.is_some()
}
pub fn is_alive(&self) -> bool {
self.ms_to_first_response.is_some()
}
}