1use candid::CandidType;
7use serde::Deserialize;
8
9use crate::runtime::now_millis;
10
11use super::{EntityCounters, EventOps, with_state};
12
13pub mod compact_metric_code {
19 pub const LOAD_CALLS: u16 = 1;
21 pub const SAVE_CALLS: u16 = 2;
23 pub const DELETE_CALLS: u16 = 3;
25 pub const EXEC_SUCCESS: u16 = 4;
27 pub const EXEC_ERRORS: u16 = 5;
29 pub const EXEC_ABORTED: u16 = 6;
31 pub const ROWS_LOADED: u16 = 7;
33 pub const ROWS_SAVED: u16 = 8;
35 pub const ROWS_DELETED: u16 = 9;
37 pub const ROWS_SCANNED: u16 = 10;
39 pub const ROWS_FILTERED: u16 = 11;
41 pub const ROWS_EMITTED: u16 = 12;
43 pub const SQL_INSERT_CALLS: u16 = 13;
45 pub const SQL_INSERT_SELECT_CALLS: u16 = 14;
47 pub const SQL_UPDATE_CALLS: u16 = 15;
49 pub const SQL_DELETE_CALLS: u16 = 16;
51 pub const SQL_WRITE_MATCHED_ROWS: u16 = 17;
53 pub const SQL_WRITE_MUTATED_ROWS: u16 = 18;
55 pub const SQL_WRITE_RETURNING_ROWS: u16 = 19;
57 pub const CACHE_SHARED_QUERY_PLAN_HITS: u16 = 20;
59 pub const CACHE_SHARED_QUERY_PLAN_MISSES: u16 = 21;
61 pub const CACHE_SQL_COMPILED_COMMAND_HITS: u16 = 22;
63 pub const CACHE_SQL_COMPILED_COMMAND_MISSES: u16 = 23;
65 pub const SQL_WRITE_STAGED_ROWS: u16 = 24;
67}
68
69#[cfg_attr(doc, doc = "CompactMetric\n\nCompact metrics counter.")]
70#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
71pub struct CompactMetric(u16, u64);
72
73impl CompactMetric {
74 #[must_use]
75 const fn new(code: u16, value: u64) -> Self {
76 Self(code, value)
77 }
78
79 #[must_use]
81 pub const fn code(&self) -> u16 {
82 self.0
83 }
84
85 #[must_use]
87 pub const fn value(&self) -> u64 {
88 self.1
89 }
90
91 #[must_use]
93 pub const fn into_code_and_value(self) -> (u16, u64) {
94 (self.0, self.1)
95 }
96}
97
98#[cfg_attr(doc, doc = "CompactEventCounters\n\nCompact global metrics counters.")]
99#[derive(CandidType, Clone, Debug, Default, Deserialize, Eq, PartialEq)]
100pub struct CompactEventCounters(Vec<CompactMetric>, u64, u64);
101
102impl CompactEventCounters {
103 #[must_use]
104 const fn new(metrics: Vec<CompactMetric>, window_start_ms: u64, window_end_ms: u64) -> Self {
105 Self(metrics, window_start_ms, window_end_ms)
106 }
107
108 #[must_use]
110 pub fn metrics(&self) -> &[CompactMetric] {
111 &self.0
112 }
113
114 #[must_use]
116 pub const fn window_start_ms(&self) -> u64 {
117 self.1
118 }
119
120 #[must_use]
122 pub const fn window_end_ms(&self) -> u64 {
123 self.2
124 }
125
126 #[must_use]
128 pub const fn window_duration_ms(&self) -> u64 {
129 self.2.saturating_sub(self.1)
130 }
131}
132
133#[cfg_attr(
134 doc,
135 doc = "CompactEntityMetrics\n\nCompact per-entity metrics counters."
136)]
137#[derive(CandidType, Clone, Debug, Default, Deserialize, Eq, PartialEq)]
138pub struct CompactEntityMetrics(String, Vec<CompactMetric>);
139
140impl CompactEntityMetrics {
141 #[must_use]
142 const fn new(path: String, metrics: Vec<CompactMetric>) -> Self {
143 Self(path, metrics)
144 }
145
146 #[must_use]
148 pub const fn path(&self) -> &str {
149 self.0.as_str()
150 }
151
152 #[must_use]
154 pub fn metrics(&self) -> &[CompactMetric] {
155 &self.1
156 }
157}
158
159#[cfg_attr(doc, doc = "CompactMetricsReport\n\nCompact metrics query payload.")]
160#[derive(CandidType, Clone, Debug, Default, Deserialize, Eq, PartialEq)]
161pub struct CompactMetricsReport(
162 Option<CompactEventCounters>,
163 Vec<CompactEntityMetrics>,
164 Option<u64>,
165 u64,
166);
167
168impl CompactMetricsReport {
169 #[must_use]
170 const fn new(
171 counters: Option<CompactEventCounters>,
172 entity_counters: Vec<CompactEntityMetrics>,
173 requested_window_start_ms: Option<u64>,
174 active_window_start_ms: u64,
175 ) -> Self {
176 Self(
177 counters,
178 entity_counters,
179 requested_window_start_ms,
180 active_window_start_ms,
181 )
182 }
183
184 #[must_use]
186 pub const fn counters(&self) -> Option<&CompactEventCounters> {
187 self.0.as_ref()
188 }
189
190 #[must_use]
192 pub fn entity_counters(&self) -> &[CompactEntityMetrics] {
193 &self.1
194 }
195
196 #[must_use]
198 pub const fn window_filter_matched(&self) -> bool {
199 self.0.is_some()
200 }
201
202 #[must_use]
204 pub const fn requested_window_start_ms(&self) -> Option<u64> {
205 self.2
206 }
207
208 #[must_use]
210 pub const fn active_window_start_ms(&self) -> u64 {
211 self.3
212 }
213}
214
215fn push_compact_metric(metrics: &mut Vec<CompactMetric>, code: u16, value: u64) {
216 if value != 0 {
217 metrics.push(CompactMetric::new(code, value));
218 }
219}
220
221const fn event_ops_exec_errors(ops: &EventOps) -> u64 {
222 ops.exec_error_corruption
223 .saturating_add(ops.exec_error_incompatible_persisted_format)
224 .saturating_add(ops.exec_error_not_found)
225 .saturating_add(ops.exec_error_internal)
226 .saturating_add(ops.exec_error_conflict)
227 .saturating_add(ops.exec_error_unsupported)
228 .saturating_add(ops.exec_error_invariant_violation)
229}
230
231const fn entity_ops_exec_errors(ops: &EntityCounters) -> u64 {
232 ops.exec_error_corruption
233 .saturating_add(ops.exec_error_incompatible_persisted_format)
234 .saturating_add(ops.exec_error_not_found)
235 .saturating_add(ops.exec_error_internal)
236 .saturating_add(ops.exec_error_conflict)
237 .saturating_add(ops.exec_error_unsupported)
238 .saturating_add(ops.exec_error_invariant_violation)
239}
240
241fn compact_event_metrics(ops: &EventOps) -> Vec<CompactMetric> {
242 use compact_metric_code::{
243 CACHE_SHARED_QUERY_PLAN_HITS, CACHE_SHARED_QUERY_PLAN_MISSES,
244 CACHE_SQL_COMPILED_COMMAND_HITS, CACHE_SQL_COMPILED_COMMAND_MISSES, DELETE_CALLS,
245 EXEC_ABORTED, EXEC_ERRORS, EXEC_SUCCESS, LOAD_CALLS, ROWS_DELETED, ROWS_EMITTED,
246 ROWS_FILTERED, ROWS_LOADED, ROWS_SAVED, ROWS_SCANNED, SAVE_CALLS, SQL_DELETE_CALLS,
247 SQL_INSERT_CALLS, SQL_INSERT_SELECT_CALLS, SQL_UPDATE_CALLS, SQL_WRITE_MATCHED_ROWS,
248 SQL_WRITE_MUTATED_ROWS, SQL_WRITE_RETURNING_ROWS, SQL_WRITE_STAGED_ROWS,
249 };
250
251 let mut metrics = Vec::new();
252 push_compact_metric(&mut metrics, LOAD_CALLS, ops.load_calls);
253 push_compact_metric(&mut metrics, SAVE_CALLS, ops.save_calls);
254 push_compact_metric(&mut metrics, DELETE_CALLS, ops.delete_calls);
255 push_compact_metric(&mut metrics, EXEC_SUCCESS, ops.exec_success);
256 push_compact_metric(&mut metrics, EXEC_ERRORS, event_ops_exec_errors(ops));
257 push_compact_metric(&mut metrics, EXEC_ABORTED, ops.exec_aborted);
258 push_compact_metric(&mut metrics, ROWS_LOADED, ops.rows_loaded);
259 push_compact_metric(&mut metrics, ROWS_SAVED, ops.rows_saved);
260 push_compact_metric(&mut metrics, ROWS_DELETED, ops.rows_deleted);
261 push_compact_metric(&mut metrics, ROWS_SCANNED, ops.rows_scanned);
262 push_compact_metric(&mut metrics, ROWS_FILTERED, ops.rows_filtered);
263 push_compact_metric(&mut metrics, ROWS_EMITTED, ops.rows_emitted);
264 push_compact_metric(&mut metrics, SQL_INSERT_CALLS, ops.sql_insert_calls);
265 push_compact_metric(
266 &mut metrics,
267 SQL_INSERT_SELECT_CALLS,
268 ops.sql_insert_select_calls,
269 );
270 push_compact_metric(&mut metrics, SQL_UPDATE_CALLS, ops.sql_update_calls);
271 push_compact_metric(&mut metrics, SQL_DELETE_CALLS, ops.sql_delete_calls);
272 push_compact_metric(
273 &mut metrics,
274 SQL_WRITE_MATCHED_ROWS,
275 ops.sql_write_matched_rows,
276 );
277 push_compact_metric(
278 &mut metrics,
279 SQL_WRITE_MUTATED_ROWS,
280 ops.sql_write_mutated_rows,
281 );
282 push_compact_metric(
283 &mut metrics,
284 SQL_WRITE_RETURNING_ROWS,
285 ops.sql_write_returning_rows,
286 );
287 push_compact_metric(
288 &mut metrics,
289 SQL_WRITE_STAGED_ROWS,
290 ops.sql_write_staged_rows,
291 );
292 push_compact_metric(
293 &mut metrics,
294 CACHE_SHARED_QUERY_PLAN_HITS,
295 ops.cache_shared_query_plan_hits,
296 );
297 push_compact_metric(
298 &mut metrics,
299 CACHE_SHARED_QUERY_PLAN_MISSES,
300 ops.cache_shared_query_plan_misses,
301 );
302 push_compact_metric(
303 &mut metrics,
304 CACHE_SQL_COMPILED_COMMAND_HITS,
305 ops.cache_sql_compiled_command_hits,
306 );
307 push_compact_metric(
308 &mut metrics,
309 CACHE_SQL_COMPILED_COMMAND_MISSES,
310 ops.cache_sql_compiled_command_misses,
311 );
312
313 metrics
314}
315
316fn compact_entity_metrics(ops: &EntityCounters) -> Vec<CompactMetric> {
317 use compact_metric_code::{
318 CACHE_SHARED_QUERY_PLAN_HITS, CACHE_SHARED_QUERY_PLAN_MISSES,
319 CACHE_SQL_COMPILED_COMMAND_HITS, CACHE_SQL_COMPILED_COMMAND_MISSES, DELETE_CALLS,
320 EXEC_ABORTED, EXEC_ERRORS, EXEC_SUCCESS, LOAD_CALLS, ROWS_DELETED, ROWS_EMITTED,
321 ROWS_FILTERED, ROWS_LOADED, ROWS_SAVED, ROWS_SCANNED, SAVE_CALLS, SQL_DELETE_CALLS,
322 SQL_INSERT_CALLS, SQL_INSERT_SELECT_CALLS, SQL_UPDATE_CALLS, SQL_WRITE_MATCHED_ROWS,
323 SQL_WRITE_MUTATED_ROWS, SQL_WRITE_RETURNING_ROWS, SQL_WRITE_STAGED_ROWS,
324 };
325
326 let mut metrics = Vec::new();
327 push_compact_metric(&mut metrics, LOAD_CALLS, ops.load_calls);
328 push_compact_metric(&mut metrics, SAVE_CALLS, ops.save_calls);
329 push_compact_metric(&mut metrics, DELETE_CALLS, ops.delete_calls);
330 push_compact_metric(&mut metrics, EXEC_SUCCESS, ops.exec_success);
331 push_compact_metric(&mut metrics, EXEC_ERRORS, entity_ops_exec_errors(ops));
332 push_compact_metric(&mut metrics, EXEC_ABORTED, ops.exec_aborted);
333 push_compact_metric(&mut metrics, ROWS_LOADED, ops.rows_loaded);
334 push_compact_metric(&mut metrics, ROWS_SAVED, ops.rows_saved);
335 push_compact_metric(&mut metrics, ROWS_DELETED, ops.rows_deleted);
336 push_compact_metric(&mut metrics, ROWS_SCANNED, ops.rows_scanned);
337 push_compact_metric(&mut metrics, ROWS_FILTERED, ops.rows_filtered);
338 push_compact_metric(&mut metrics, ROWS_EMITTED, ops.rows_emitted);
339 push_compact_metric(&mut metrics, SQL_INSERT_CALLS, ops.sql_insert_calls);
340 push_compact_metric(
341 &mut metrics,
342 SQL_INSERT_SELECT_CALLS,
343 ops.sql_insert_select_calls,
344 );
345 push_compact_metric(&mut metrics, SQL_UPDATE_CALLS, ops.sql_update_calls);
346 push_compact_metric(&mut metrics, SQL_DELETE_CALLS, ops.sql_delete_calls);
347 push_compact_metric(
348 &mut metrics,
349 SQL_WRITE_MATCHED_ROWS,
350 ops.sql_write_matched_rows,
351 );
352 push_compact_metric(
353 &mut metrics,
354 SQL_WRITE_MUTATED_ROWS,
355 ops.sql_write_mutated_rows,
356 );
357 push_compact_metric(
358 &mut metrics,
359 SQL_WRITE_RETURNING_ROWS,
360 ops.sql_write_returning_rows,
361 );
362 push_compact_metric(
363 &mut metrics,
364 SQL_WRITE_STAGED_ROWS,
365 ops.sql_write_staged_rows,
366 );
367 push_compact_metric(
368 &mut metrics,
369 CACHE_SHARED_QUERY_PLAN_HITS,
370 ops.cache_shared_query_plan_hits,
371 );
372 push_compact_metric(
373 &mut metrics,
374 CACHE_SHARED_QUERY_PLAN_MISSES,
375 ops.cache_shared_query_plan_misses,
376 );
377 push_compact_metric(
378 &mut metrics,
379 CACHE_SQL_COMPILED_COMMAND_HITS,
380 ops.cache_sql_compiled_command_hits,
381 );
382 push_compact_metric(
383 &mut metrics,
384 CACHE_SQL_COMPILED_COMMAND_MISSES,
385 ops.cache_sql_compiled_command_misses,
386 );
387
388 metrics
389}
390
391#[must_use]
393pub(in crate::metrics) fn compact_report_window_start(
394 window_start_ms: Option<u64>,
395) -> CompactMetricsReport {
396 let snap = with_state(Clone::clone);
397 if let Some(requested_window_start_ms) = window_start_ms
398 && requested_window_start_ms > snap.window_start_ms
399 {
400 return CompactMetricsReport::new(None, Vec::new(), window_start_ms, snap.window_start_ms);
401 }
402
403 let mut entity_counters: Vec<CompactEntityMetrics> = Vec::new();
404 for (path, ops) in &snap.entities {
405 let metrics = compact_entity_metrics(ops);
406 if !metrics.is_empty() {
407 entity_counters.push(CompactEntityMetrics::new(path.clone(), metrics));
408 }
409 }
410
411 entity_counters.sort_by(|a, b| {
412 let a_activity = compact_metrics_activity(a.metrics());
413 let b_activity = compact_metrics_activity(b.metrics());
414 b_activity
415 .cmp(&a_activity)
416 .then_with(|| a.path().cmp(b.path()))
417 });
418
419 CompactMetricsReport::new(
420 Some(CompactEventCounters::new(
421 compact_event_metrics(&snap.ops),
422 snap.window_start_ms,
423 now_millis(),
424 )),
425 entity_counters,
426 window_start_ms,
427 snap.window_start_ms,
428 )
429}
430
431fn compact_metrics_activity(metrics: &[CompactMetric]) -> u64 {
432 metrics
433 .iter()
434 .fold(0, |total, metric| total.saturating_add(metric.value()))
435}