use hdrhistogram::Histogram;
use std::sync::atomic::{AtomicU64, Ordering};
pub struct EngineMetrics {
pub triggers: AtomicU64,
pub registrations: AtomicU64,
pub updates: AtomicU64,
pub reloads: AtomicU64,
pub decide_latency_ns: parking_lot::Mutex<Histogram<u64>>,
}
impl EngineMetrics {
#[allow(clippy::expect_used)]
#[must_use]
pub fn new() -> Self {
Self {
triggers: AtomicU64::new(0),
registrations: AtomicU64::new(0),
updates: AtomicU64::new(0),
reloads: AtomicU64::new(0),
decide_latency_ns: parking_lot::Mutex::new(
Histogram::new_with_bounds(1, 10_000_000_000, 3)
.expect("histogram bounds (1, 10_000_000_000, 3) are always valid"),
),
}
}
#[inline]
pub fn record_decide_latency_ns(&self, ns: u64) {
let clamped = ns.min(10_000_000_000);
if let Some(mut hist) = self.decide_latency_ns.try_lock() {
let _ = hist.record(clamped);
}
}
#[must_use]
pub fn snapshot(&self) -> MetricsSnapshot {
let hist = self.decide_latency_ns.lock();
MetricsSnapshot {
triggers: self.triggers.load(Ordering::Relaxed),
registrations: self.registrations.load(Ordering::Relaxed),
updates: self.updates.load(Ordering::Relaxed),
reloads: self.reloads.load(Ordering::Relaxed),
decide_latency_ns_p50: hist.value_at_quantile(0.50),
decide_latency_ns_p99: hist.value_at_quantile(0.99),
decide_latency_ns_max: hist.max(),
}
}
}
impl Default for EngineMetrics {
fn default() -> Self {
Self::new()
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Debug, Clone)]
pub struct MetricsSnapshot {
pub triggers: u64,
pub registrations: u64,
pub updates: u64,
pub reloads: u64,
pub decide_latency_ns_p50: u64,
pub decide_latency_ns_p99: u64,
pub decide_latency_ns_max: u64,
}