//! Six Sigma Prometheus CTQ metrics. use crate::AppState; use axum::{extract::State, response::IntoResponse}; use std::sync::atomic::{AtomicU64, Ordering}; pub struct CtqMetrics { pub uploads_total: AtomicU64, pub defects_total: AtomicU64, pub band_common: AtomicU64, pub band_rare: AtomicU64, pub band_legendary: AtomicU64, latency_sum_ms: AtomicU64, latency_count: AtomicU64, } impl Default for CtqMetrics { fn default() -> Self { Self::new() } } impl CtqMetrics { pub fn new() -> Self { Self { uploads_total: AtomicU64::new(0), defects_total: AtomicU64::new(0), band_common: AtomicU64::new(0), band_rare: AtomicU64::new(0), band_legendary: AtomicU64::new(0), latency_sum_ms: AtomicU64::new(0), latency_count: AtomicU64::new(0), } } pub fn record_defect(&self, _kind: &str) { self.defects_total.fetch_add(1, Ordering::Relaxed); } pub fn record_band(&self, band: u8) { self.uploads_total.fetch_add(1, Ordering::Relaxed); match band { 0 => self.band_common.fetch_add(1, Ordering::Relaxed), 1 => self.band_rare.fetch_add(1, Ordering::Relaxed), _ => self.band_legendary.fetch_add(1, Ordering::Relaxed), }; } pub fn record_latency(&self, _name: &str, ms: f64) { self.latency_sum_ms.fetch_add(ms as u64, Ordering::Relaxed); self.latency_count.fetch_add(1, Ordering::Relaxed); } pub fn band_distribution_in_control(&self) -> bool { let total = self.uploads_total.load(Ordering::Relaxed); if total < 30 { return true; } let common = self.band_common.load(Ordering::Relaxed) as f64 / total as f64; (common - 7.0 / 15.0).abs() <= 0.15 } pub fn metrics_text(&self) -> String { let up = self.uploads_total.load(Ordering::Relaxed); let de = self.defects_total.load(Ordering::Relaxed); let dpmo = if up > 0 { de * 1_000_000 / up } else { 0 }; format!( "# HELP retrosync_uploads_total Total uploads\n\ retrosync_uploads_total {up}\n\ retrosync_defects_total {de}\n\ retrosync_dpmo {dpmo}\n\ retrosync_band_common {}\n\ retrosync_band_rare {}\n\ retrosync_band_legendary {}\n\ retrosync_band_in_control {}\n", self.band_common.load(Ordering::Relaxed), self.band_rare.load(Ordering::Relaxed), self.band_legendary.load(Ordering::Relaxed), self.band_distribution_in_control() as u8, ) } } pub async fn handler(State(state): State) -> impl IntoResponse { state.metrics.metrics_text() }