use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Clone, Default)]
pub struct RuntimeMetricsStorage {
pub(crate) boxes_created: Arc<AtomicU64>,
pub(crate) boxes_failed: Arc<AtomicU64>,
pub(crate) boxes_stopped: Arc<AtomicU64>,
pub(crate) total_commands: Arc<AtomicU64>,
pub(crate) total_exec_errors: Arc<AtomicU64>,
}
impl RuntimeMetricsStorage {
pub fn new() -> Self {
Self::default()
}
}
#[derive(Clone)]
pub struct RuntimeMetrics {
storage: RuntimeMetricsStorage,
}
impl RuntimeMetrics {
pub(crate) fn new(storage: RuntimeMetricsStorage) -> Self {
Self { storage }
}
pub fn boxes_created_total(&self) -> u64 {
self.storage.boxes_created.load(Ordering::Relaxed)
}
pub fn boxes_failed_total(&self) -> u64 {
self.storage.boxes_failed.load(Ordering::Relaxed)
}
pub fn boxes_stopped_total(&self) -> u64 {
self.storage.boxes_stopped.load(Ordering::Relaxed)
}
pub fn num_running_boxes(&self) -> u64 {
let created = self.boxes_created_total();
let stopped = self.boxes_stopped_total();
let failed = self.boxes_failed_total();
created.saturating_sub(stopped).saturating_sub(failed)
}
pub fn total_commands_executed(&self) -> u64 {
self.storage.total_commands.load(Ordering::Relaxed)
}
pub fn total_exec_errors(&self) -> u64 {
self.storage.total_exec_errors.load(Ordering::Relaxed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_num_running_boxes_calculation() {
let storage = RuntimeMetricsStorage::new();
let metrics = RuntimeMetrics::new(storage.clone());
assert_eq!(metrics.num_running_boxes(), 0);
for _ in 0..5 {
storage.boxes_created.fetch_add(1, Ordering::Relaxed);
}
assert_eq!(metrics.num_running_boxes(), 5);
storage.boxes_stopped.fetch_add(1, Ordering::Relaxed);
storage.boxes_stopped.fetch_add(1, Ordering::Relaxed);
assert_eq!(metrics.num_running_boxes(), 3);
storage.boxes_created.fetch_add(1, Ordering::Relaxed);
storage.boxes_failed.fetch_add(1, Ordering::Relaxed);
assert_eq!(metrics.num_running_boxes(), 3);
for _ in 0..3 {
storage.boxes_stopped.fetch_add(1, Ordering::Relaxed);
}
assert_eq!(metrics.num_running_boxes(), 0);
}
#[test]
fn test_num_running_boxes_saturating_sub() {
let storage = RuntimeMetricsStorage::new();
let metrics = RuntimeMetrics::new(storage.clone());
storage.boxes_created.fetch_add(1, Ordering::Relaxed);
storage.boxes_stopped.fetch_add(5, Ordering::Relaxed);
assert_eq!(metrics.num_running_boxes(), 0);
}
#[test]
fn test_boxes_stopped_total() {
let storage = RuntimeMetricsStorage::new();
let metrics = RuntimeMetrics::new(storage.clone());
assert_eq!(metrics.boxes_stopped_total(), 0);
storage.boxes_stopped.fetch_add(3, Ordering::Relaxed);
assert_eq!(metrics.boxes_stopped_total(), 3);
}
}