use alloc::string::String;
use core::sync::atomic::{AtomicU64, Ordering};
use super::{Distributio, Indicium, Numerator};
#[derive(Debug)]
pub struct MensuraFibrae {
spawned: Numerator,
completed: Numerator,
failed: Numerator,
cancelled: Numerator,
active: Indicium,
lifetime: Distributio,
peak_active: AtomicU64,
}
impl MensuraFibrae {
pub fn new() -> Self {
MensuraFibrae {
spawned: Numerator::new(),
completed: Numerator::new(),
failed: Numerator::new(),
cancelled: Numerator::new(),
active: Indicium::new(),
lifetime: Distributio::new(),
peak_active: AtomicU64::new(0),
}
}
#[inline]
pub fn record_spawn(&self) {
self.spawned.increment();
self.active.increment();
let current = self.active.value();
let mut peak = self.peak_active.load(Ordering::Relaxed);
while current > peak {
match self.peak_active.compare_exchange_weak(
peak,
current,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => break,
Err(p) => peak = p,
}
}
}
#[inline]
pub fn record_completed(&self, lifetime_ns: u64) {
self.completed.increment();
self.active.decrement();
self.lifetime.record(lifetime_ns);
}
#[inline]
pub fn record_failed(&self, lifetime_ns: u64) {
self.failed.increment();
self.active.decrement();
self.lifetime.record(lifetime_ns);
}
#[inline]
pub fn record_cancelled(&self, lifetime_ns: u64) {
self.cancelled.increment();
self.active.decrement();
self.lifetime.record(lifetime_ns);
}
#[inline]
pub fn total_spawned(&self) -> u64 {
self.spawned.value()
}
#[inline]
pub fn completed_count(&self) -> u64 {
self.completed.value()
}
#[inline]
pub fn failed_count(&self) -> u64 {
self.failed.value()
}
#[inline]
pub fn cancelled_count(&self) -> u64 {
self.cancelled.value()
}
#[inline]
pub fn active_count(&self) -> u64 {
self.active.value()
}
#[inline]
pub fn peak_active(&self) -> u64 {
self.peak_active.load(Ordering::Relaxed)
}
#[inline]
pub fn mean_lifetime_ns(&self) -> f64 {
self.lifetime.mean()
}
#[inline]
pub fn mean_lifetime_ms(&self) -> f64 {
self.lifetime.mean() / 1_000_000.0
}
#[inline]
pub fn lifetime_percentile_ns(&self, p: f64) -> Option<u64> {
self.lifetime.percentile(p)
}
#[inline]
#[allow(clippy::cast_precision_loss)]
pub fn success_rate(&self) -> f64 {
let total_finished = self.completed_count() + self.failed_count() + self.cancelled_count();
if total_finished == 0 {
1.0
} else {
self.completed_count() as f64 / total_finished as f64
}
}
pub fn summary(&self) -> FiberMetricsSummary {
FiberMetricsSummary {
total_spawned: self.total_spawned(),
completed: self.completed_count(),
failed: self.failed_count(),
cancelled: self.cancelled_count(),
active: self.active_count(),
peak_active: self.peak_active(),
mean_lifetime_ns: self.mean_lifetime_ns(),
}
}
}
impl Default for MensuraFibrae {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct FiberMetricsSummary {
pub total_spawned: u64,
pub completed: u64,
pub failed: u64,
pub cancelled: u64,
pub active: u64,
pub peak_active: u64,
pub mean_lifetime_ns: f64,
}
#[derive(Debug)]
pub struct MensuraArboris {
name: String,
restarts: Numerator,
intensity_violations: Numerator,
healthy_children: Indicium,
total_children: Indicium,
time_since_failure: AtomicU64,
}
impl MensuraArboris {
pub fn new(name: impl Into<String>) -> Self {
MensuraArboris {
name: name.into(),
restarts: Numerator::new(),
intensity_violations: Numerator::new(),
healthy_children: Indicium::new(),
total_children: Indicium::new(),
time_since_failure: AtomicU64::new(0),
}
}
#[inline]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
pub fn record_restart(&self) {
self.restarts.increment();
self.time_since_failure.store(0, Ordering::Relaxed);
}
#[inline]
pub fn record_intensity_violation(&self) {
self.intensity_violations.increment();
}
#[inline]
pub fn set_child_counts(&self, healthy: u64, total: u64) {
self.healthy_children.set(healthy);
self.total_children.set(total);
}
#[inline]
pub fn restart_count(&self) -> u64 {
self.restarts.value()
}
#[inline]
pub fn intensity_violation_count(&self) -> u64 {
self.intensity_violations.value()
}
#[inline]
pub fn healthy_children(&self) -> u64 {
self.healthy_children.value()
}
#[inline]
pub fn total_children(&self) -> u64 {
self.total_children.value()
}
#[inline]
#[allow(clippy::cast_precision_loss)]
pub fn health_percentage(&self) -> f64 {
let total = self.total_children();
if total == 0 {
100.0
} else {
(self.healthy_children() as f64 / total as f64) * 100.0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mensura_fibrae_spawn() {
let metrics = MensuraFibrae::new();
metrics.record_spawn();
metrics.record_spawn();
assert_eq!(metrics.total_spawned(), 2);
assert_eq!(metrics.active_count(), 2);
assert_eq!(metrics.peak_active(), 2);
}
#[test]
fn test_mensura_fibrae_completion() {
let metrics = MensuraFibrae::new();
metrics.record_spawn();
metrics.record_spawn();
metrics.record_completed(1_000_000);
assert_eq!(metrics.active_count(), 1);
assert_eq!(metrics.completed_count(), 1);
assert_eq!(metrics.peak_active(), 2);
}
#[test]
fn test_mensura_fibrae_failures() {
let metrics = MensuraFibrae::new();
metrics.record_spawn();
metrics.record_failed(500_000);
assert_eq!(metrics.failed_count(), 1);
assert_eq!(metrics.active_count(), 0);
}
#[test]
fn test_mensura_arboris() {
let metrics = MensuraArboris::new("supervisor");
metrics.set_child_counts(3, 5);
metrics.record_restart();
assert_eq!(metrics.restart_count(), 1);
assert_eq!(metrics.health_percentage(), 60.0);
}
}