use crate::queue::key::RefreshKey;
thread_local! {
static METRICS: std::cell::RefCell<QueueMetrics> = const { std::cell::RefCell::new(QueueMetrics::new_const()) };
}
#[derive(Debug, Default, Clone)]
struct QueueMetrics {
total_refreshes: u64,
total_iterations: u64,
max_iterations: usize,
total_timing_ns: u128,
graph_cache_hits: u64,
graph_cache_misses: u64,
table_cache_hits: u64,
table_cache_misses: u64,
prepared_stmt_cache_hits: u64,
prepared_stmt_cache_misses: u64,
bulk_refresh_count: u64,
individual_refresh_count: u64,
}
impl QueueMetrics {
const fn new_const() -> Self {
Self {
total_refreshes: 0,
total_iterations: 0,
max_iterations: 0,
total_timing_ns: 0,
graph_cache_hits: 0,
graph_cache_misses: 0,
table_cache_hits: 0,
table_cache_misses: 0,
prepared_stmt_cache_hits: 0,
prepared_stmt_cache_misses: 0,
bulk_refresh_count: 0,
individual_refresh_count: 0,
}
}
}
pub mod metrics_api {
#[allow(clippy::wildcard_imports)] use super::*;
pub fn record_refresh_start() -> RefreshTimer {
RefreshTimer::new()
}
pub fn record_refresh_complete(
refresh_count: usize,
iteration_count: usize,
timer: &RefreshTimer,
) {
METRICS.with(|m| {
let mut metrics = m.borrow_mut();
metrics.total_refreshes += refresh_count as u64;
metrics.total_iterations += iteration_count as u64;
metrics.max_iterations = metrics.max_iterations.max(iteration_count);
metrics.total_timing_ns += timer.elapsed_ns();
});
}
pub fn record_graph_cache_hit() {
METRICS.with(|m| {
m.borrow_mut().graph_cache_hits += 1;
});
}
pub fn record_graph_cache_miss() {
METRICS.with(|m| {
m.borrow_mut().graph_cache_misses += 1;
});
}
pub fn record_table_cache_hit() {
METRICS.with(|m| {
m.borrow_mut().table_cache_hits += 1;
});
}
pub fn record_table_cache_miss() {
METRICS.with(|m| {
m.borrow_mut().table_cache_misses += 1;
});
}
#[allow(dead_code)] pub fn record_prepared_stmt_cache_hit() {
METRICS.with(|m| {
m.borrow_mut().prepared_stmt_cache_hits += 1;
});
}
#[allow(dead_code)] pub fn record_prepared_stmt_cache_miss() {
METRICS.with(|m| {
m.borrow_mut().prepared_stmt_cache_misses += 1;
});
}
#[allow(dead_code)] pub fn record_bulk_refresh(count: usize) {
METRICS.with(|m| {
let mut metrics = m.borrow_mut();
metrics.bulk_refresh_count += 1;
metrics.total_refreshes += count as u64;
});
}
#[allow(dead_code)] pub fn record_individual_refresh() {
METRICS.with(|m| {
let mut metrics = m.borrow_mut();
metrics.individual_refresh_count += 1;
metrics.total_refreshes += 1;
});
}
pub fn get_queue_stats() -> QueueStats {
let queue_size = crate::queue::get_queue_size();
METRICS.with(|m| {
let metrics = m.borrow();
QueueStats {
queue_size,
total_refreshes: metrics.total_refreshes,
total_iterations: metrics.total_iterations,
max_iterations: metrics.max_iterations,
total_timing_ns: metrics.total_timing_ns,
graph_cache_hits: metrics.graph_cache_hits,
graph_cache_misses: metrics.graph_cache_misses,
table_cache_hits: metrics.table_cache_hits,
table_cache_misses: metrics.table_cache_misses,
prepared_stmt_cache_hits: metrics.prepared_stmt_cache_hits,
prepared_stmt_cache_misses: metrics.prepared_stmt_cache_misses,
bulk_refresh_count: metrics.bulk_refresh_count,
individual_refresh_count: metrics.individual_refresh_count,
}
})
}
pub fn get_queue_contents() -> Vec<RefreshKey> {
crate::queue::get_queue_contents()
}
pub fn reset_metrics() {
METRICS.with(|m| {
*m.borrow_mut() = QueueMetrics::default();
});
}
}
pub struct RefreshTimer {
start: std::time::Instant,
}
impl RefreshTimer {
fn new() -> Self {
Self {
start: std::time::Instant::now(),
}
}
fn elapsed_ns(&self) -> u128 {
self.start.elapsed().as_nanos()
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)] pub struct QueueStats {
pub queue_size: usize,
pub total_refreshes: u64,
pub total_iterations: u64,
pub max_iterations: usize,
pub total_timing_ns: u128,
pub graph_cache_hits: u64,
pub graph_cache_misses: u64,
pub table_cache_hits: u64,
pub table_cache_misses: u64,
pub prepared_stmt_cache_hits: u64,
pub prepared_stmt_cache_misses: u64,
pub bulk_refresh_count: u64,
pub individual_refresh_count: u64,
}
impl QueueStats {
#[allow(clippy::cast_precision_loss)]
pub fn total_timing_ms(&self) -> f64 {
self.total_timing_ns as f64 / 1_000_000.0
}
#[allow(clippy::cast_precision_loss)]
pub fn graph_cache_hit_rate(&self) -> f64 {
let total = self.graph_cache_hits + self.graph_cache_misses;
if total == 0 {
0.0
} else {
self.graph_cache_hits as f64 / total as f64
}
}
#[allow(clippy::cast_precision_loss)]
pub fn table_cache_hit_rate(&self) -> f64 {
let total = self.table_cache_hits + self.table_cache_misses;
if total == 0 {
0.0
} else {
self.table_cache_hits as f64 / total as f64
}
}
}