use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::time::{Duration, Instant};
const PERCENTAGE_PRECISION_FACTOR: f64 = 100.0;
const MAX_SUCCESS_RATE: f64 = 100.0;
const DEFAULT_UTILIZATION: f64 = 0.0;
#[derive(Debug)]
pub struct ExecutorMetrics {
pub tasks_spawned: AtomicU64,
pub tasks_completed: AtomicU64,
pub tasks_failed: AtomicU64,
pub tasks_cancelled: AtomicU64,
pub total_execution_time: AtomicU64,
pub active_workers: AtomicUsize,
pub idle_workers: AtomicUsize,
pub total_workers: AtomicUsize,
pub pending_tasks: AtomicUsize,
pub max_queue_depth: AtomicUsize,
pub started_at: Instant,
pub last_updated_after_ns: AtomicU64,
}
impl ExecutorMetrics {
pub fn new() -> Self {
let now = Instant::now();
Self {
tasks_spawned: AtomicU64::new(0),
tasks_completed: AtomicU64::new(0),
tasks_failed: AtomicU64::new(0),
tasks_cancelled: AtomicU64::new(0),
total_execution_time: AtomicU64::new(0),
active_workers: AtomicUsize::new(0),
idle_workers: AtomicUsize::new(0),
total_workers: AtomicUsize::new(0),
pending_tasks: AtomicUsize::new(0),
max_queue_depth: AtomicUsize::new(0),
started_at: now,
last_updated_after_ns: AtomicU64::new(0),
}
}
pub fn record_task_spawned(&self) {
self.tasks_spawned.fetch_add(1, Ordering::Relaxed);
self.update_timestamp();
}
pub fn record_task_completed(&self, execution_time: Duration) {
let exec_nanos = execution_time.as_nanos() as u64;
self.tasks_completed.fetch_add(1, Ordering::Relaxed);
self.total_execution_time
.fetch_add(exec_nanos, Ordering::Relaxed);
self.update_timestamp();
}
pub fn record_task_failed(&self) {
self.tasks_failed.fetch_add(1, Ordering::Relaxed);
self.update_timestamp();
}
pub fn record_task_cancelled(&self) {
self.tasks_cancelled.fetch_add(1, Ordering::Relaxed);
self.update_timestamp();
}
pub fn update_worker_counts(&self, active: usize, idle: usize, total: usize) {
self.active_workers.store(active, Ordering::Relaxed);
self.idle_workers.store(idle, Ordering::Relaxed);
self.total_workers.store(total, Ordering::Relaxed);
self.update_timestamp();
}
pub fn update_queue_metrics(&self, pending: usize) {
self.pending_tasks.store(pending, Ordering::Relaxed);
let current_max = self.max_queue_depth.load(Ordering::Relaxed);
if pending > current_max {
self.max_queue_depth.store(pending, Ordering::Relaxed);
}
self.update_timestamp();
}
pub fn throughput(&self) -> f64 {
let elapsed = self.started_at.elapsed().as_secs_f64();
if elapsed > 0.0 {
self.tasks_completed.load(Ordering::Relaxed) as f64 / elapsed
} else {
0.0
}
}
pub fn success_rate(&self) -> f64 {
let completed = self.tasks_completed.load(Ordering::Relaxed);
let failed = self.tasks_failed.load(Ordering::Relaxed);
let total = completed + failed;
if total > 0 {
(completed as f64 / total as f64) * PERCENTAGE_PRECISION_FACTOR
} else {
MAX_SUCCESS_RATE
}
}
pub fn average_task_duration(&self) -> Duration {
let completed = self.tasks_completed.load(Ordering::Relaxed);
let average = self
.total_execution_time
.load(Ordering::Relaxed)
.checked_div(completed)
.unwrap_or(0);
Duration::from_nanos(average)
}
pub fn worker_utilization(&self) -> f64 {
let total = self.total_workers.load(Ordering::Relaxed);
if total > 0 {
let active = self.active_workers.load(Ordering::Relaxed);
(active as f64 / total as f64) * PERCENTAGE_PRECISION_FACTOR
} else {
DEFAULT_UTILIZATION
}
}
pub fn uptime(&self) -> Duration {
self.started_at.elapsed()
}
pub fn last_updated(&self) -> Instant {
self.started_at
.checked_add(Duration::from_nanos(
self.last_updated_after_ns.load(Ordering::Relaxed),
))
.unwrap_or(self.started_at)
}
fn update_timestamp(&self) {
self.last_updated_after_ns
.store(elapsed_nanos_since(self.started_at), Ordering::Relaxed);
}
pub fn reset(&self) {
self.tasks_spawned.store(0, Ordering::Relaxed);
self.tasks_completed.store(0, Ordering::Relaxed);
self.tasks_failed.store(0, Ordering::Relaxed);
self.tasks_cancelled.store(0, Ordering::Relaxed);
self.total_execution_time.store(0, Ordering::Relaxed);
self.active_workers.store(0, Ordering::Relaxed);
self.idle_workers.store(0, Ordering::Relaxed);
self.total_workers.store(0, Ordering::Relaxed);
self.pending_tasks.store(0, Ordering::Relaxed);
self.max_queue_depth.store(0, Ordering::Relaxed);
self.update_timestamp();
}
}
fn elapsed_nanos_since(origin: Instant) -> u64 {
origin.elapsed().as_nanos().min(u128::from(u64::MAX)) as u64
}
impl Default for ExecutorMetrics {
fn default() -> Self {
Self::new()
}
}