use std::sync::atomic::{AtomicU64, Ordering};
pub struct DataScopeMetrics {
hit_total: AtomicU64,
bypass_total: AtomicU64,
reject_total: AtomicU64,
eval_total_ms: AtomicU64,
eval_count: AtomicU64,
}
impl DataScopeMetrics {
pub fn new() -> Self {
Self {
hit_total: AtomicU64::new(0),
bypass_total: AtomicU64::new(0),
reject_total: AtomicU64::new(0),
eval_total_ms: AtomicU64::new(0),
eval_count: AtomicU64::new(0),
}
}
pub fn record_hit(&self, table: &str, mode: &str) {
self.hit_total.fetch_add(1, Ordering::Relaxed);
tracing::debug!(
target: "data_scope_metrics",
table = table,
mode = mode,
"data_scope_hit"
);
}
pub fn record_bypass(&self, user_id: i64, table: &str) {
self.bypass_total.fetch_add(1, Ordering::Relaxed);
tracing::info!(
target: "data_scope_audit",
user_id = user_id,
table = table,
"super bypass: user_id={}, table={}", user_id, table
);
}
pub fn record_reject(&self, error_code: &str, table: &str) {
self.reject_total.fetch_add(1, Ordering::Relaxed);
tracing::warn!(
target: "data_scope_metrics",
error_code = error_code,
table = table,
"data_scope_reject"
);
}
pub fn record_eval(&self, elapsed_ms: u64) {
self.eval_total_ms.fetch_add(elapsed_ms, Ordering::Relaxed);
self.eval_count.fetch_add(1, Ordering::Relaxed);
}
pub fn hit_total(&self) -> u64 {
self.hit_total.load(Ordering::Relaxed)
}
pub fn bypass_total(&self) -> u64 {
self.bypass_total.load(Ordering::Relaxed)
}
pub fn reject_total(&self) -> u64 {
self.reject_total.load(Ordering::Relaxed)
}
pub fn avg_eval_ms(&self) -> f64 {
let count = self.eval_count.load(Ordering::Relaxed);
if count == 0 {
0.0
} else {
self.eval_total_ms.load(Ordering::Relaxed) as f64 / count as f64
}
}
}
impl Default for DataScopeMetrics {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metrics_record() {
let metrics = DataScopeMetrics::new();
metrics.record_hit("order", "dept");
metrics.record_hit("order", "dept");
metrics.record_bypass(1, "order");
metrics.record_reject("DATA_SCOPE_NO_USER_CONTEXT", "order");
metrics.record_eval(5);
metrics.record_eval(15);
assert_eq!(metrics.hit_total(), 2);
assert_eq!(metrics.bypass_total(), 1);
assert_eq!(metrics.reject_total(), 1);
assert_eq!(metrics.avg_eval_ms(), 10.0);
}
#[test]
fn test_avg_eval_ms_zero_count() {
let metrics = DataScopeMetrics::new();
assert_eq!(metrics.avg_eval_ms(), 0.0);
}
#[test]
fn test_default() {
let metrics = DataScopeMetrics::default();
assert_eq!(metrics.hit_total(), 0);
assert_eq!(metrics.avg_eval_ms(), 0.0);
}
}