1use serde::{Deserialize, Serialize};
3
4use super::core::StatsManager;
5use super::metric_type::MetricType;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct SearchIndexBreakdown {
10 pub index: String,
11 pub search_queries: u64,
12 pub search_errors: u64,
13 pub search_latency_ms: u64,
14 pub avg_search_latency_ms: f64,
15 pub index_operations: u64,
16 pub index_errors: u64,
17 pub index_latency_ms: u64,
18}
19
20impl StatsManager {
21 pub fn record_search(&self, space_id: u64, index_name: &str, latency_ms: u64, success: bool) {
25 let space_key = Self::space_key(space_id);
26 let index_key = Self::index_key(space_id, index_name);
27 self.add_value(MetricType::NumSearchQueries);
28 self.add_space_metric(&space_key, MetricType::NumSearchQueries);
29 self.add_index_metric(&index_key, MetricType::NumSearchQueries);
30
31 if !success {
32 self.add_value(MetricType::NumSearchErrors);
33 self.add_space_metric(&space_key, MetricType::NumSearchErrors);
34 self.add_index_metric(&index_key, MetricType::NumSearchErrors);
35 }
36
37 self.add_value_with_amount(MetricType::SearchLatencyMs, latency_ms);
38 self.add_space_metric_with_amount(&space_key, MetricType::SearchLatencyMs, latency_ms);
39 self.add_index_metric_with_amount(&index_key, MetricType::SearchLatencyMs, latency_ms);
40
41 {
42 let mut histogram = self.search_latency_histogram.write();
43 histogram.record_micros(latency_ms * 1000);
44 }
45 }
46
47 pub fn record_index_operation(
49 &self,
50 space_id: u64,
51 index_name: &str,
52 latency_ms: u64,
53 success: bool,
54 ) {
55 let space_key = Self::space_key(space_id);
56 let index_key = Self::index_key(space_id, index_name);
57 self.add_value(MetricType::NumIndexOperations);
58 self.add_space_metric(&space_key, MetricType::NumIndexOperations);
59 self.add_index_metric(&index_key, MetricType::NumIndexOperations);
60
61 if !success {
62 self.add_value(MetricType::NumIndexErrors);
63 self.add_space_metric(&space_key, MetricType::NumIndexErrors);
64 self.add_index_metric(&index_key, MetricType::NumIndexErrors);
65 }
66
67 self.add_value_with_amount(MetricType::IndexLatencyMs, latency_ms);
68 self.add_space_metric_with_amount(&space_key, MetricType::IndexLatencyMs, latency_ms);
69 self.add_index_metric_with_amount(&index_key, MetricType::IndexLatencyMs, latency_ms);
70 }
71
72 pub fn record_index_operation_unknown_space(
76 &self,
77 index_name: &str,
78 latency_ms: u64,
79 success: bool,
80 ) {
81 let index_key = Self::unknown_index_key(index_name);
82 self.add_value(MetricType::NumIndexOperations);
83 self.add_index_metric(&index_key, MetricType::NumIndexOperations);
84 if !success {
85 self.add_value(MetricType::NumIndexErrors);
86 self.add_index_metric(&index_key, MetricType::NumIndexErrors);
87 }
88 self.add_value_with_amount(MetricType::IndexLatencyMs, latency_ms);
89 self.add_index_metric_with_amount(&index_key, MetricType::IndexLatencyMs, latency_ms);
90 }
91
92 pub fn record_delete_operation(
94 &self,
95 space_id: u64,
96 index_name: &str,
97 latency_ms: u64,
98 success: bool,
99 ) {
100 let space_key = Self::space_key(space_id);
101 let index_key = Self::index_key(space_id, index_name);
102 self.add_value(MetricType::NumDeleteOperations);
103 self.add_space_metric(&space_key, MetricType::NumDeleteOperations);
104 self.add_index_metric(&index_key, MetricType::NumDeleteOperations);
105
106 if !success {
107 self.add_value(MetricType::NumDeleteErrors);
108 self.add_space_metric(&space_key, MetricType::NumDeleteErrors);
109 self.add_index_metric(&index_key, MetricType::NumDeleteErrors);
110 }
111
112 self.add_value_with_amount(MetricType::DeleteLatencyMs, latency_ms);
113 self.add_space_metric_with_amount(&space_key, MetricType::DeleteLatencyMs, latency_ms);
114 self.add_index_metric_with_amount(&index_key, MetricType::DeleteLatencyMs, latency_ms);
115 }
116
117 pub fn record_search_result_count(&self, space_id: u64, count: u64) {
119 let space_key = Self::space_key(space_id);
120 self.add_value_with_amount(MetricType::SearchResultCount, count);
121 self.add_space_metric_with_amount(&space_key, MetricType::SearchResultCount, count);
122 }
123
124 pub fn record_cache_hit_global(&self, hit: bool) {
126 if hit {
127 self.add_value(MetricType::SearchCacheHitCount);
128 } else {
129 self.add_value(MetricType::SearchCacheMissCount);
130 }
131 }
132
133 pub fn record_vector_disabled_skips(&self, count: u64) {
134 self.set_value(MetricType::VectorDisabledSkips, count);
135 }
136
137 pub fn record_vector_embedding(
143 &self,
144 prompt_tokens: u64,
145 total_tokens: u64,
146 latency_ms: u64,
147 success: bool,
148 ) {
149 self.add_value(MetricType::VectorEmbeddingOps);
150 if !success {
151 self.add_value(MetricType::VectorEmbeddingErrors);
152 }
153 self.add_value_with_amount(MetricType::VectorEmbeddingLatencyMs, latency_ms);
154 self.add_value_with_amount(MetricType::VectorEmbeddingPromptTokens, prompt_tokens);
155 self.add_value_with_amount(MetricType::VectorEmbeddingTotalTokens, total_tokens);
156 }
157
158 pub fn record_vector_rerank(&self, latency_ms: u64, success: bool) {
163 self.add_value(MetricType::VectorRerankOps);
164 if !success {
165 self.add_value(MetricType::VectorRerankErrors);
166 }
167 self.add_value_with_amount(MetricType::VectorRerankLatencyMs, latency_ms);
168 }
169
170 pub fn search_index_breakdown(&self) -> Vec<SearchIndexBreakdown> {
171 let mut out = Vec::new();
172 for entry in self.index_metrics.iter() {
173 let name = entry.key().clone();
174 let map = entry.value();
175 let get = |m: MetricType| map.get(&m).map(|v| v.get()).unwrap_or(0);
176 let search_queries = get(MetricType::NumSearchQueries);
177 let index_operations = get(MetricType::NumIndexOperations);
178 if search_queries == 0 && index_operations == 0 {
179 continue;
180 }
181 let search_latency_ms = get(MetricType::SearchLatencyMs);
182 out.push(SearchIndexBreakdown {
183 index: name,
184 search_queries,
185 search_errors: get(MetricType::NumSearchErrors),
186 search_latency_ms,
187 avg_search_latency_ms: if search_queries == 0 {
188 0.0
189 } else {
190 search_latency_ms as f64 / search_queries as f64
191 },
192 index_operations,
193 index_errors: get(MetricType::NumIndexErrors),
194 index_latency_ms: get(MetricType::IndexLatencyMs),
195 });
196 }
197 out.sort_by_key(|s| std::cmp::Reverse(s.search_queries));
198 out.truncate(50);
199 out
200 }
201}