Skip to main content

linkrs_metrics/manager/
query_metrics.rs

1//! Lightweight numeric query metrics and latency histograms.
2use serde::{Deserialize, Serialize};
3use std::sync::Arc;
4
5use crate::metrics::QueryMetrics;
6
7use super::core::StatsManager;
8use super::metric_type::MetricType;
9use super::metric_value::MetricValue;
10
11/// Graph query latency snapshot derived from the shared histogram.
12#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
13pub struct QueryLatencySnapshot {
14    pub count: usize,
15    pub avg_us: u64,
16    pub p50_us: u64,
17    pub p95_us: u64,
18    pub p99_us: u64,
19}
20
21impl StatsManager {
22    pub fn record_query_metrics(&self, metrics: &QueryMetrics) {
23        // Per-instance history instead of single-slot overwrite: concurrent
24        // queries append under the lock so no instance is lost.
25        {
26            let mut last_metrics = self.last_query_metrics.write();
27            *last_metrics = Some(metrics.clone());
28        }
29        {
30            let mut history = self.query_metrics_history.write();
31            if history.len() >= 1000 {
32                history.pop_front();
33            }
34            history.push_back(metrics.clone());
35        }
36
37        // Record latency histogram
38        {
39            let mut histogram = self.query_latency_histogram.write();
40            histogram.record_micros(metrics.total_time_us);
41        }
42
43        // Cumulative sums plus a query count so means are computable;
44        // never overwrite with a single query's values.
45        let updates = [
46            (MetricType::QueryParseTimeUs, metrics.parse_time_us),
47            (MetricType::QueryValidateTimeUs, metrics.validate_time_us),
48            (MetricType::QueryPlanTimeUs, metrics.plan_time_us),
49            (MetricType::QueryOptimizeTimeUs, metrics.optimize_time_us),
50            (MetricType::QueryExecuteTimeUs, metrics.execute_time_us),
51            (MetricType::QueryTotalTimeUs, metrics.total_time_us),
52            (
53                MetricType::QueryPlanNodeCount,
54                metrics.plan_node_count as u64,
55            ),
56            (
57                MetricType::QueryResultRowCount,
58                metrics.result_row_count as u64,
59            ),
60        ];
61
62        for (metric_type, value) in updates {
63            let metric = self
64                .metrics
65                .entry(metric_type)
66                .or_insert_with(|| Arc::new(MetricValue::new(0)));
67            metric.add(value);
68        }
69        self.add_value(MetricType::NumQueries);
70    }
71
72    /// Get latency percentiles (avg, p50, p95, p99) in microseconds
73    pub fn get_latency_percentiles(&self) -> (u64, u64, u64, u64) {
74        let histogram = self.query_latency_histogram.read();
75        (
76            histogram.avg(),
77            histogram.p50(),
78            histogram.p95(),
79            histogram.p99(),
80        )
81    }
82
83    pub fn query_latency_snapshot(&self) -> QueryLatencySnapshot {
84        let histogram = self.query_latency_histogram.read();
85        QueryLatencySnapshot {
86            count: histogram.count(),
87            avg_us: histogram.avg(),
88            p50_us: histogram.p50(),
89            p95_us: histogram.p95(),
90            p99_us: histogram.p99(),
91        }
92    }
93
94    /// Get search latency percentiles (avg, p50, p95, p99) in microseconds
95    pub fn get_search_latency_percentiles(&self) -> (u64, u64, u64, u64) {
96        let histogram = self.search_latency_histogram.read();
97        (
98            histogram.avg(),
99            histogram.p50(),
100            histogram.p95(),
101            histogram.p99(),
102        )
103    }
104
105    /// Get latency histogram report
106    pub fn get_latency_report(&self) -> String {
107        let histogram = self.query_latency_histogram.read();
108        histogram.report()
109    }
110
111    /// Clear latency histogram
112    pub fn clear_latency_histogram(&self) {
113        let mut histogram = self.query_latency_histogram.write();
114        histogram.clear();
115    }
116
117    pub fn get_last_query_metrics(&self) -> Option<QueryMetrics> {
118        let last_metrics = self.last_query_metrics.read();
119        last_metrics.clone()
120    }
121
122    /// Recent per-instance query metrics (up to 1000), newest last.
123    pub fn get_query_metrics_history(&self) -> Vec<QueryMetrics> {
124        self.query_metrics_history.read().iter().cloned().collect()
125    }
126
127    pub fn get_query_metrics(&self) -> Option<QueryMetrics> {
128        self.get_last_query_metrics()
129    }
130}