1use candid::CandidType;
11use canic_cdk::utils::time::now_millis;
12use serde::Deserialize;
13use std::{cell::RefCell, collections::BTreeMap};
14
15#[derive(Clone, Debug)]
16pub(crate) struct EventState {
17 pub(crate) ops: EventOps,
18 pub(crate) perf: EventPerf,
19 pub(crate) entities: BTreeMap<String, EntityCounters>,
20 pub(crate) window_start_ms: u64,
21}
22
23impl Default for EventState {
24 fn default() -> Self {
25 Self {
26 ops: EventOps::default(),
27 perf: EventPerf::default(),
28 entities: BTreeMap::new(),
29 window_start_ms: now_millis(),
30 }
31 }
32}
33
34#[derive(Clone, Copy, Debug, Eq, PartialEq)]
42pub struct MetricRatio {
43 numerator: u64,
44 denominator: u64,
45}
46
47impl MetricRatio {
48 #[must_use]
50 pub const fn numerator(&self) -> u64 {
51 self.numerator
52 }
53
54 #[must_use]
56 pub const fn denominator(&self) -> u64 {
57 self.denominator
58 }
59
60 #[must_use]
62 pub const fn into_parts(self) -> (u64, u64) {
63 (self.numerator, self.denominator)
64 }
65}
66
67const fn ratio(numerator: u64, denominator: u64) -> Option<MetricRatio> {
71 if denominator == 0 {
72 return None;
73 }
74
75 Some(MetricRatio {
76 numerator,
77 denominator,
78 })
79}
80
81#[cfg_attr(doc, doc = "EventOps\n\nOperation counters.")]
82#[derive(CandidType, Clone, Debug, Default, Deserialize)]
83pub struct EventOps {
84 pub(crate) load_calls: u64,
86 pub(crate) save_calls: u64,
87 pub(crate) delete_calls: u64,
88 pub(crate) save_insert_calls: u64,
89 pub(crate) save_update_calls: u64,
90 pub(crate) save_replace_calls: u64,
91 pub(crate) exec_success: u64,
92 pub(crate) exec_error_corruption: u64,
93 pub(crate) exec_error_incompatible_persisted_format: u64,
94 pub(crate) exec_error_not_found: u64,
95 pub(crate) exec_error_internal: u64,
96 pub(crate) exec_error_conflict: u64,
97 pub(crate) exec_error_unsupported: u64,
98 pub(crate) exec_error_invariant_violation: u64,
99 pub(crate) exec_aborted: u64,
100 pub(crate) cache_shared_query_plan_hits: u64,
101 pub(crate) cache_shared_query_plan_misses: u64,
102 pub(crate) cache_shared_query_plan_inserts: u64,
103 pub(crate) cache_shared_query_plan_entries: u64,
104 pub(crate) cache_sql_compiled_command_hits: u64,
105 pub(crate) cache_sql_compiled_command_misses: u64,
106 pub(crate) cache_sql_compiled_command_inserts: u64,
107 pub(crate) cache_sql_compiled_command_entries: u64,
108
109 pub(crate) plan_index: u64,
111 pub(crate) plan_keys: u64,
112 pub(crate) plan_range: u64,
113 pub(crate) plan_full_scan: u64,
114 pub(crate) plan_by_key: u64,
115 pub(crate) plan_by_keys: u64,
116 pub(crate) plan_key_range: u64,
117 pub(crate) plan_index_prefix: u64,
118 pub(crate) plan_index_multi_lookup: u64,
119 pub(crate) plan_index_range: u64,
120 pub(crate) plan_explicit_full_scan: u64,
121 pub(crate) plan_union: u64,
122 pub(crate) plan_intersection: u64,
123 pub(crate) plan_grouped_hash_materialized: u64,
124 pub(crate) plan_grouped_ordered_materialized: u64,
125
126 pub(crate) rows_loaded: u64,
128 pub(crate) rows_saved: u64,
129 pub(crate) rows_inserted: u64,
130 pub(crate) rows_updated: u64,
131 pub(crate) rows_replaced: u64,
132 pub(crate) rows_scanned: u64,
133 pub(crate) rows_filtered: u64,
134 pub(crate) rows_aggregated: u64,
135 pub(crate) rows_emitted: u64,
136 pub(crate) load_candidate_rows_scanned: u64,
137 pub(crate) load_candidate_rows_filtered: u64,
138 pub(crate) load_result_rows_emitted: u64,
139 pub(crate) rows_deleted: u64,
140 pub(crate) sql_insert_calls: u64,
141 pub(crate) sql_insert_select_calls: u64,
142 pub(crate) sql_update_calls: u64,
143 pub(crate) sql_delete_calls: u64,
144 pub(crate) sql_write_matched_rows: u64,
145 pub(crate) sql_write_mutated_rows: u64,
146 pub(crate) sql_write_returning_rows: u64,
147 pub(crate) sql_write_error_insert: u64,
148 pub(crate) sql_write_error_insert_select: u64,
149 pub(crate) sql_write_error_update: u64,
150 pub(crate) sql_write_error_delete: u64,
151 pub(crate) sql_write_error_corruption: u64,
152 pub(crate) sql_write_error_incompatible_persisted_format: u64,
153 pub(crate) sql_write_error_not_found: u64,
154 pub(crate) sql_write_error_internal: u64,
155 pub(crate) sql_write_error_conflict: u64,
156 pub(crate) sql_write_error_unsupported: u64,
157 pub(crate) sql_write_error_invariant_violation: u64,
158
159 pub(crate) index_inserts: u64,
161 pub(crate) index_removes: u64,
162 pub(crate) reverse_index_inserts: u64,
163 pub(crate) reverse_index_removes: u64,
164 pub(crate) relation_reverse_lookups: u64,
165 pub(crate) relation_delete_blocks: u64,
166 pub(crate) write_rows_touched: u64,
167 pub(crate) write_index_entries_changed: u64,
168 pub(crate) write_reverse_index_entries_changed: u64,
169 pub(crate) write_relation_checks: u64,
170 pub(crate) unique_violations: u64,
171 pub(crate) non_atomic_partial_commits: u64,
172 pub(crate) non_atomic_partial_rows_committed: u64,
173}
174
175impl EventOps {
176 #[must_use]
177 pub const fn load_calls(&self) -> u64 {
178 self.load_calls
179 }
180
181 #[must_use]
182 pub const fn save_calls(&self) -> u64 {
183 self.save_calls
184 }
185
186 #[must_use]
187 pub const fn delete_calls(&self) -> u64 {
188 self.delete_calls
189 }
190
191 #[must_use]
192 pub const fn save_insert_calls(&self) -> u64 {
193 self.save_insert_calls
194 }
195
196 #[must_use]
197 pub const fn save_update_calls(&self) -> u64 {
198 self.save_update_calls
199 }
200
201 #[must_use]
202 pub const fn save_replace_calls(&self) -> u64 {
203 self.save_replace_calls
204 }
205
206 #[must_use]
207 pub const fn exec_success(&self) -> u64 {
208 self.exec_success
209 }
210
211 #[must_use]
212 pub const fn exec_error_corruption(&self) -> u64 {
213 self.exec_error_corruption
214 }
215
216 #[must_use]
217 pub const fn exec_error_incompatible_persisted_format(&self) -> u64 {
218 self.exec_error_incompatible_persisted_format
219 }
220
221 #[must_use]
222 pub const fn exec_error_not_found(&self) -> u64 {
223 self.exec_error_not_found
224 }
225
226 #[must_use]
227 pub const fn exec_error_internal(&self) -> u64 {
228 self.exec_error_internal
229 }
230
231 #[must_use]
232 pub const fn exec_error_conflict(&self) -> u64 {
233 self.exec_error_conflict
234 }
235
236 #[must_use]
237 pub const fn exec_error_unsupported(&self) -> u64 {
238 self.exec_error_unsupported
239 }
240
241 #[must_use]
242 pub const fn exec_error_invariant_violation(&self) -> u64 {
243 self.exec_error_invariant_violation
244 }
245
246 #[must_use]
247 pub const fn exec_aborted(&self) -> u64 {
248 self.exec_aborted
249 }
250
251 #[must_use]
252 pub const fn cache_shared_query_plan_hits(&self) -> u64 {
253 self.cache_shared_query_plan_hits
254 }
255
256 #[must_use]
257 pub const fn cache_shared_query_plan_misses(&self) -> u64 {
258 self.cache_shared_query_plan_misses
259 }
260
261 #[must_use]
262 pub const fn cache_shared_query_plan_inserts(&self) -> u64 {
263 self.cache_shared_query_plan_inserts
264 }
265
266 #[must_use]
267 pub const fn cache_shared_query_plan_entries(&self) -> u64 {
268 self.cache_shared_query_plan_entries
269 }
270
271 #[must_use]
272 pub const fn cache_sql_compiled_command_hits(&self) -> u64 {
273 self.cache_sql_compiled_command_hits
274 }
275
276 #[must_use]
277 pub const fn cache_sql_compiled_command_misses(&self) -> u64 {
278 self.cache_sql_compiled_command_misses
279 }
280
281 #[must_use]
282 pub const fn cache_sql_compiled_command_inserts(&self) -> u64 {
283 self.cache_sql_compiled_command_inserts
284 }
285
286 #[must_use]
287 pub const fn cache_sql_compiled_command_entries(&self) -> u64 {
288 self.cache_sql_compiled_command_entries
289 }
290
291 #[must_use]
292 pub const fn plan_index(&self) -> u64 {
293 self.plan_index
294 }
295
296 #[must_use]
297 pub const fn plan_keys(&self) -> u64 {
298 self.plan_keys
299 }
300
301 #[must_use]
302 pub const fn plan_range(&self) -> u64 {
303 self.plan_range
304 }
305
306 #[must_use]
307 pub const fn plan_full_scan(&self) -> u64 {
308 self.plan_full_scan
309 }
310
311 #[must_use]
312 pub const fn plan_by_key(&self) -> u64 {
313 self.plan_by_key
314 }
315
316 #[must_use]
317 pub const fn plan_by_keys(&self) -> u64 {
318 self.plan_by_keys
319 }
320
321 #[must_use]
322 pub const fn plan_key_range(&self) -> u64 {
323 self.plan_key_range
324 }
325
326 #[must_use]
327 pub const fn plan_index_prefix(&self) -> u64 {
328 self.plan_index_prefix
329 }
330
331 #[must_use]
332 pub const fn plan_index_multi_lookup(&self) -> u64 {
333 self.plan_index_multi_lookup
334 }
335
336 #[must_use]
337 pub const fn plan_index_range(&self) -> u64 {
338 self.plan_index_range
339 }
340
341 #[must_use]
342 pub const fn plan_explicit_full_scan(&self) -> u64 {
343 self.plan_explicit_full_scan
344 }
345
346 #[must_use]
347 pub const fn plan_union(&self) -> u64 {
348 self.plan_union
349 }
350
351 #[must_use]
352 pub const fn plan_intersection(&self) -> u64 {
353 self.plan_intersection
354 }
355
356 #[must_use]
357 pub const fn plan_grouped_hash_materialized(&self) -> u64 {
358 self.plan_grouped_hash_materialized
359 }
360
361 #[must_use]
362 pub const fn plan_grouped_ordered_materialized(&self) -> u64 {
363 self.plan_grouped_ordered_materialized
364 }
365
366 #[must_use]
367 pub const fn rows_loaded(&self) -> u64 {
368 self.rows_loaded
369 }
370
371 #[must_use]
372 pub const fn rows_saved(&self) -> u64 {
373 self.rows_saved
374 }
375
376 #[must_use]
377 pub const fn rows_inserted(&self) -> u64 {
378 self.rows_inserted
379 }
380
381 #[must_use]
382 pub const fn rows_updated(&self) -> u64 {
383 self.rows_updated
384 }
385
386 #[must_use]
387 pub const fn rows_replaced(&self) -> u64 {
388 self.rows_replaced
389 }
390
391 #[must_use]
392 pub const fn rows_scanned(&self) -> u64 {
393 self.rows_scanned
394 }
395
396 #[must_use]
397 pub const fn rows_filtered(&self) -> u64 {
398 self.rows_filtered
399 }
400
401 #[must_use]
402 pub const fn rows_aggregated(&self) -> u64 {
403 self.rows_aggregated
404 }
405
406 #[must_use]
407 pub const fn rows_emitted(&self) -> u64 {
408 self.rows_emitted
409 }
410
411 #[must_use]
412 pub const fn load_candidate_rows_scanned(&self) -> u64 {
413 self.load_candidate_rows_scanned
414 }
415
416 #[must_use]
417 pub const fn load_candidate_rows_filtered(&self) -> u64 {
418 self.load_candidate_rows_filtered
419 }
420
421 #[must_use]
422 pub const fn load_result_rows_emitted(&self) -> u64 {
423 self.load_result_rows_emitted
424 }
425
426 #[must_use]
427 pub const fn rows_deleted(&self) -> u64 {
428 self.rows_deleted
429 }
430
431 #[must_use]
432 pub const fn sql_insert_calls(&self) -> u64 {
433 self.sql_insert_calls
434 }
435
436 #[must_use]
437 pub const fn sql_insert_select_calls(&self) -> u64 {
438 self.sql_insert_select_calls
439 }
440
441 #[must_use]
442 pub const fn sql_update_calls(&self) -> u64 {
443 self.sql_update_calls
444 }
445
446 #[must_use]
447 pub const fn sql_delete_calls(&self) -> u64 {
448 self.sql_delete_calls
449 }
450
451 #[must_use]
452 pub const fn sql_write_matched_rows(&self) -> u64 {
453 self.sql_write_matched_rows
454 }
455
456 #[must_use]
457 pub const fn sql_write_mutated_rows(&self) -> u64 {
458 self.sql_write_mutated_rows
459 }
460
461 #[must_use]
462 pub const fn sql_write_returning_rows(&self) -> u64 {
463 self.sql_write_returning_rows
464 }
465
466 #[must_use]
467 pub const fn sql_write_error_insert(&self) -> u64 {
468 self.sql_write_error_insert
469 }
470
471 #[must_use]
472 pub const fn sql_write_error_insert_select(&self) -> u64 {
473 self.sql_write_error_insert_select
474 }
475
476 #[must_use]
477 pub const fn sql_write_error_update(&self) -> u64 {
478 self.sql_write_error_update
479 }
480
481 #[must_use]
482 pub const fn sql_write_error_delete(&self) -> u64 {
483 self.sql_write_error_delete
484 }
485
486 #[must_use]
487 pub const fn sql_write_error_corruption(&self) -> u64 {
488 self.sql_write_error_corruption
489 }
490
491 #[must_use]
492 pub const fn sql_write_error_incompatible_persisted_format(&self) -> u64 {
493 self.sql_write_error_incompatible_persisted_format
494 }
495
496 #[must_use]
497 pub const fn sql_write_error_not_found(&self) -> u64 {
498 self.sql_write_error_not_found
499 }
500
501 #[must_use]
502 pub const fn sql_write_error_internal(&self) -> u64 {
503 self.sql_write_error_internal
504 }
505
506 #[must_use]
507 pub const fn sql_write_error_conflict(&self) -> u64 {
508 self.sql_write_error_conflict
509 }
510
511 #[must_use]
512 pub const fn sql_write_error_unsupported(&self) -> u64 {
513 self.sql_write_error_unsupported
514 }
515
516 #[must_use]
517 pub const fn sql_write_error_invariant_violation(&self) -> u64 {
518 self.sql_write_error_invariant_violation
519 }
520
521 #[must_use]
522 pub const fn index_inserts(&self) -> u64 {
523 self.index_inserts
524 }
525
526 #[must_use]
527 pub const fn index_removes(&self) -> u64 {
528 self.index_removes
529 }
530
531 #[must_use]
532 pub const fn reverse_index_inserts(&self) -> u64 {
533 self.reverse_index_inserts
534 }
535
536 #[must_use]
537 pub const fn reverse_index_removes(&self) -> u64 {
538 self.reverse_index_removes
539 }
540
541 #[must_use]
542 pub const fn relation_reverse_lookups(&self) -> u64 {
543 self.relation_reverse_lookups
544 }
545
546 #[must_use]
547 pub const fn relation_delete_blocks(&self) -> u64 {
548 self.relation_delete_blocks
549 }
550
551 #[must_use]
552 pub const fn write_rows_touched(&self) -> u64 {
553 self.write_rows_touched
554 }
555
556 #[must_use]
557 pub const fn write_index_entries_changed(&self) -> u64 {
558 self.write_index_entries_changed
559 }
560
561 #[must_use]
562 pub const fn write_reverse_index_entries_changed(&self) -> u64 {
563 self.write_reverse_index_entries_changed
564 }
565
566 #[must_use]
567 pub const fn write_relation_checks(&self) -> u64 {
568 self.write_relation_checks
569 }
570
571 #[must_use]
572 pub const fn unique_violations(&self) -> u64 {
573 self.unique_violations
574 }
575
576 #[must_use]
577 pub const fn non_atomic_partial_commits(&self) -> u64 {
578 self.non_atomic_partial_commits
579 }
580
581 #[must_use]
582 pub const fn non_atomic_partial_rows_committed(&self) -> u64 {
583 self.non_atomic_partial_rows_committed
584 }
585
586 #[must_use]
588 pub const fn load_selectivity_ratio(&self) -> Option<MetricRatio> {
589 ratio(
590 self.load_result_rows_emitted,
591 self.load_candidate_rows_scanned,
592 )
593 }
594
595 #[must_use]
597 pub const fn load_filter_ratio(&self) -> Option<MetricRatio> {
598 ratio(
599 self.load_candidate_rows_filtered,
600 self.load_candidate_rows_scanned,
601 )
602 }
603
604 #[must_use]
606 pub const fn sql_write_mutation_ratio(&self) -> Option<MetricRatio> {
607 ratio(self.sql_write_mutated_rows, self.sql_write_matched_rows)
608 }
609
610 #[must_use]
612 pub const fn sql_write_returning_ratio(&self) -> Option<MetricRatio> {
613 ratio(self.sql_write_returning_rows, self.sql_write_mutated_rows)
614 }
615
616 #[must_use]
618 pub const fn write_index_entries_per_row(&self) -> Option<MetricRatio> {
619 ratio(self.write_index_entries_changed, self.write_rows_touched)
620 }
621
622 #[must_use]
624 pub const fn write_reverse_index_entries_per_row(&self) -> Option<MetricRatio> {
625 ratio(
626 self.write_reverse_index_entries_changed,
627 self.write_rows_touched,
628 )
629 }
630
631 #[must_use]
633 pub const fn write_relation_checks_per_row(&self) -> Option<MetricRatio> {
634 ratio(self.write_relation_checks, self.write_rows_touched)
635 }
636}
637
638#[derive(Clone, Debug, Default)]
639pub(crate) struct EntityCounters {
640 pub(crate) load_calls: u64,
641 pub(crate) save_calls: u64,
642 pub(crate) delete_calls: u64,
643 pub(crate) save_insert_calls: u64,
644 pub(crate) save_update_calls: u64,
645 pub(crate) save_replace_calls: u64,
646 pub(crate) exec_success: u64,
647 pub(crate) exec_error_corruption: u64,
648 pub(crate) exec_error_incompatible_persisted_format: u64,
649 pub(crate) exec_error_not_found: u64,
650 pub(crate) exec_error_internal: u64,
651 pub(crate) exec_error_conflict: u64,
652 pub(crate) exec_error_unsupported: u64,
653 pub(crate) exec_error_invariant_violation: u64,
654 pub(crate) exec_aborted: u64,
655 pub(crate) cache_shared_query_plan_hits: u64,
656 pub(crate) cache_shared_query_plan_misses: u64,
657 pub(crate) cache_shared_query_plan_inserts: u64,
658 pub(crate) cache_sql_compiled_command_hits: u64,
659 pub(crate) cache_sql_compiled_command_misses: u64,
660 pub(crate) cache_sql_compiled_command_inserts: u64,
661 pub(crate) plan_index: u64,
662 pub(crate) plan_keys: u64,
663 pub(crate) plan_range: u64,
664 pub(crate) plan_full_scan: u64,
665 pub(crate) plan_by_key: u64,
666 pub(crate) plan_by_keys: u64,
667 pub(crate) plan_key_range: u64,
668 pub(crate) plan_index_prefix: u64,
669 pub(crate) plan_index_multi_lookup: u64,
670 pub(crate) plan_index_range: u64,
671 pub(crate) plan_explicit_full_scan: u64,
672 pub(crate) plan_union: u64,
673 pub(crate) plan_intersection: u64,
674 pub(crate) plan_grouped_hash_materialized: u64,
675 pub(crate) plan_grouped_ordered_materialized: u64,
676 pub(crate) rows_loaded: u64,
677 pub(crate) rows_saved: u64,
678 pub(crate) rows_inserted: u64,
679 pub(crate) rows_updated: u64,
680 pub(crate) rows_replaced: u64,
681 pub(crate) rows_scanned: u64,
682 pub(crate) rows_filtered: u64,
683 pub(crate) rows_aggregated: u64,
684 pub(crate) rows_emitted: u64,
685 pub(crate) load_candidate_rows_scanned: u64,
686 pub(crate) load_candidate_rows_filtered: u64,
687 pub(crate) load_result_rows_emitted: u64,
688 pub(crate) rows_deleted: u64,
689 pub(crate) sql_insert_calls: u64,
690 pub(crate) sql_insert_select_calls: u64,
691 pub(crate) sql_update_calls: u64,
692 pub(crate) sql_delete_calls: u64,
693 pub(crate) sql_write_matched_rows: u64,
694 pub(crate) sql_write_mutated_rows: u64,
695 pub(crate) sql_write_returning_rows: u64,
696 pub(crate) sql_write_error_insert: u64,
697 pub(crate) sql_write_error_insert_select: u64,
698 pub(crate) sql_write_error_update: u64,
699 pub(crate) sql_write_error_delete: u64,
700 pub(crate) sql_write_error_corruption: u64,
701 pub(crate) sql_write_error_incompatible_persisted_format: u64,
702 pub(crate) sql_write_error_not_found: u64,
703 pub(crate) sql_write_error_internal: u64,
704 pub(crate) sql_write_error_conflict: u64,
705 pub(crate) sql_write_error_unsupported: u64,
706 pub(crate) sql_write_error_invariant_violation: u64,
707 pub(crate) index_inserts: u64,
708 pub(crate) index_removes: u64,
709 pub(crate) reverse_index_inserts: u64,
710 pub(crate) reverse_index_removes: u64,
711 pub(crate) relation_reverse_lookups: u64,
712 pub(crate) relation_delete_blocks: u64,
713 pub(crate) write_rows_touched: u64,
714 pub(crate) write_index_entries_changed: u64,
715 pub(crate) write_reverse_index_entries_changed: u64,
716 pub(crate) write_relation_checks: u64,
717 pub(crate) unique_violations: u64,
718 pub(crate) non_atomic_partial_commits: u64,
719 pub(crate) non_atomic_partial_rows_committed: u64,
720}
721
722#[cfg_attr(doc, doc = "EventPerf\n\nInstruction totals and maxima.")]
723#[derive(CandidType, Clone, Debug, Default, Deserialize)]
724pub struct EventPerf {
725 pub(crate) load_inst_total: u128,
727 pub(crate) save_inst_total: u128,
728 pub(crate) delete_inst_total: u128,
729
730 pub(crate) load_inst_max: u64,
732 pub(crate) save_inst_max: u64,
733 pub(crate) delete_inst_max: u64,
734}
735
736impl EventPerf {
737 #[must_use]
738 pub const fn new(
739 load_inst_total: u128,
740 save_inst_total: u128,
741 delete_inst_total: u128,
742 load_inst_max: u64,
743 save_inst_max: u64,
744 delete_inst_max: u64,
745 ) -> Self {
746 Self {
747 load_inst_total,
748 save_inst_total,
749 delete_inst_total,
750 load_inst_max,
751 save_inst_max,
752 delete_inst_max,
753 }
754 }
755
756 #[must_use]
757 pub const fn load_inst_total(&self) -> u128 {
758 self.load_inst_total
759 }
760
761 #[must_use]
762 pub const fn save_inst_total(&self) -> u128 {
763 self.save_inst_total
764 }
765
766 #[must_use]
767 pub const fn delete_inst_total(&self) -> u128 {
768 self.delete_inst_total
769 }
770
771 #[must_use]
772 pub const fn load_inst_max(&self) -> u64 {
773 self.load_inst_max
774 }
775
776 #[must_use]
777 pub const fn save_inst_max(&self) -> u64 {
778 self.save_inst_max
779 }
780
781 #[must_use]
782 pub const fn delete_inst_max(&self) -> u64 {
783 self.delete_inst_max
784 }
785}
786
787thread_local! {
788 static EVENT_STATE: RefCell<EventState> = RefCell::new(EventState::default());
789}
790
791pub(crate) fn with_state<R>(f: impl FnOnce(&EventState) -> R) -> R {
793 EVENT_STATE.with(|m| f(&m.borrow()))
794}
795
796pub(crate) fn with_state_mut<R>(f: impl FnOnce(&mut EventState) -> R) -> R {
798 EVENT_STATE.with(|m| f(&mut m.borrow_mut()))
799}
800
801pub(super) fn reset() {
803 with_state_mut(|m| *m = EventState::default());
804}
805
806pub(crate) fn reset_all() {
808 reset();
809}
810
811pub(super) fn add_instructions(total: &mut u128, max: &mut u64, delta_inst: u64) {
813 *total = total.saturating_add(u128::from(delta_inst));
814 if delta_inst > *max {
815 *max = delta_inst;
816 }
817}
818
819#[cfg_attr(doc, doc = "EventReport\n\nMetrics query payload.")]
820#[derive(CandidType, Clone, Debug, Default, Deserialize)]
821pub struct EventReport {
822 counters: Option<EventCounters>,
823 entity_counters: Vec<EntitySummary>,
824 window_filter_matched: bool,
825 requested_window_start_ms: Option<u64>,
826 active_window_start_ms: u64,
827}
828
829impl EventReport {
830 #[must_use]
831 pub(crate) const fn new(
832 counters: Option<EventCounters>,
833 entity_counters: Vec<EntitySummary>,
834 window_filter_matched: bool,
835 requested_window_start_ms: Option<u64>,
836 active_window_start_ms: u64,
837 ) -> Self {
838 Self {
839 counters,
840 entity_counters,
841 window_filter_matched,
842 requested_window_start_ms,
843 active_window_start_ms,
844 }
845 }
846
847 #[must_use]
848 pub const fn counters(&self) -> Option<&EventCounters> {
849 self.counters.as_ref()
850 }
851
852 #[must_use]
853 pub fn entity_counters(&self) -> &[EntitySummary] {
854 &self.entity_counters
855 }
856
857 #[must_use]
858 pub const fn window_filter_matched(&self) -> bool {
859 self.window_filter_matched
860 }
861
862 #[must_use]
863 pub const fn requested_window_start_ms(&self) -> Option<u64> {
864 self.requested_window_start_ms
865 }
866
867 #[must_use]
868 pub const fn active_window_start_ms(&self) -> u64 {
869 self.active_window_start_ms
870 }
871
872 #[must_use]
873 pub fn into_counters(self) -> Option<EventCounters> {
874 self.counters
875 }
876
877 #[must_use]
878 pub fn into_entity_counters(self) -> Vec<EntitySummary> {
879 self.entity_counters
880 }
881}
882
883#[derive(CandidType, Clone, Debug, Default, Deserialize)]
892pub struct EventCounters {
893 pub(crate) ops: EventOps,
894 pub(crate) perf: EventPerf,
895 pub(crate) window_start_ms: u64,
896 pub(crate) window_end_ms: u64,
897 pub(crate) window_duration_ms: u64,
898}
899
900impl EventCounters {
901 #[must_use]
902 pub(crate) const fn new(
903 ops: EventOps,
904 perf: EventPerf,
905 window_start_ms: u64,
906 window_end_ms: u64,
907 ) -> Self {
908 Self {
909 ops,
910 perf,
911 window_start_ms,
912 window_end_ms,
913 window_duration_ms: window_end_ms.saturating_sub(window_start_ms),
914 }
915 }
916
917 #[must_use]
918 pub const fn ops(&self) -> &EventOps {
919 &self.ops
920 }
921
922 #[must_use]
923 pub const fn perf(&self) -> &EventPerf {
924 &self.perf
925 }
926
927 #[must_use]
928 pub const fn window_start_ms(&self) -> u64 {
929 self.window_start_ms
930 }
931
932 #[must_use]
933 pub const fn window_end_ms(&self) -> u64 {
934 self.window_end_ms
935 }
936
937 #[must_use]
938 pub const fn window_duration_ms(&self) -> u64 {
939 self.window_duration_ms
940 }
941}
942
943#[cfg_attr(doc, doc = "EntitySummary\n\nPer-entity metrics summary.")]
944#[derive(CandidType, Clone, Debug, Default, Deserialize)]
945pub struct EntitySummary {
946 path: String,
947 load_calls: u64,
948 save_calls: u64,
949 delete_calls: u64,
950 save_insert_calls: u64,
951 save_update_calls: u64,
952 save_replace_calls: u64,
953 exec_success: u64,
954 exec_error_corruption: u64,
955 exec_error_incompatible_persisted_format: u64,
956 exec_error_not_found: u64,
957 exec_error_internal: u64,
958 exec_error_conflict: u64,
959 exec_error_unsupported: u64,
960 exec_error_invariant_violation: u64,
961 exec_aborted: u64,
962 cache_shared_query_plan_hits: u64,
963 cache_shared_query_plan_misses: u64,
964 cache_shared_query_plan_inserts: u64,
965 cache_sql_compiled_command_hits: u64,
966 cache_sql_compiled_command_misses: u64,
967 cache_sql_compiled_command_inserts: u64,
968 plan_index: u64,
969 plan_keys: u64,
970 plan_range: u64,
971 plan_full_scan: u64,
972 plan_by_key: u64,
973 plan_by_keys: u64,
974 plan_key_range: u64,
975 plan_index_prefix: u64,
976 plan_index_multi_lookup: u64,
977 plan_index_range: u64,
978 plan_explicit_full_scan: u64,
979 plan_union: u64,
980 plan_intersection: u64,
981 plan_grouped_hash_materialized: u64,
982 plan_grouped_ordered_materialized: u64,
983 rows_loaded: u64,
984 rows_saved: u64,
985 rows_inserted: u64,
986 rows_updated: u64,
987 rows_replaced: u64,
988 rows_scanned: u64,
989 rows_filtered: u64,
990 rows_aggregated: u64,
991 rows_emitted: u64,
992 load_candidate_rows_scanned: u64,
993 load_candidate_rows_filtered: u64,
994 load_result_rows_emitted: u64,
995 rows_deleted: u64,
996 sql_insert_calls: u64,
997 sql_insert_select_calls: u64,
998 sql_update_calls: u64,
999 sql_delete_calls: u64,
1000 sql_write_matched_rows: u64,
1001 sql_write_mutated_rows: u64,
1002 sql_write_returning_rows: u64,
1003 sql_write_error_insert: u64,
1004 sql_write_error_insert_select: u64,
1005 sql_write_error_update: u64,
1006 sql_write_error_delete: u64,
1007 sql_write_error_corruption: u64,
1008 sql_write_error_incompatible_persisted_format: u64,
1009 sql_write_error_not_found: u64,
1010 sql_write_error_internal: u64,
1011 sql_write_error_conflict: u64,
1012 sql_write_error_unsupported: u64,
1013 sql_write_error_invariant_violation: u64,
1014 index_inserts: u64,
1015 index_removes: u64,
1016 reverse_index_inserts: u64,
1017 reverse_index_removes: u64,
1018 relation_reverse_lookups: u64,
1019 relation_delete_blocks: u64,
1020 write_rows_touched: u64,
1021 write_index_entries_changed: u64,
1022 write_reverse_index_entries_changed: u64,
1023 write_relation_checks: u64,
1024 unique_violations: u64,
1025 non_atomic_partial_commits: u64,
1026 non_atomic_partial_rows_committed: u64,
1027}
1028
1029impl EntitySummary {
1030 #[must_use]
1031 pub const fn path(&self) -> &str {
1032 self.path.as_str()
1033 }
1034
1035 #[must_use]
1036 pub const fn load_calls(&self) -> u64 {
1037 self.load_calls
1038 }
1039
1040 #[must_use]
1041 pub const fn save_calls(&self) -> u64 {
1042 self.save_calls
1043 }
1044
1045 #[must_use]
1046 pub const fn delete_calls(&self) -> u64 {
1047 self.delete_calls
1048 }
1049
1050 #[must_use]
1051 pub const fn save_insert_calls(&self) -> u64 {
1052 self.save_insert_calls
1053 }
1054
1055 #[must_use]
1056 pub const fn save_update_calls(&self) -> u64 {
1057 self.save_update_calls
1058 }
1059
1060 #[must_use]
1061 pub const fn save_replace_calls(&self) -> u64 {
1062 self.save_replace_calls
1063 }
1064
1065 #[must_use]
1066 pub const fn exec_success(&self) -> u64 {
1067 self.exec_success
1068 }
1069
1070 #[must_use]
1071 pub const fn exec_error_corruption(&self) -> u64 {
1072 self.exec_error_corruption
1073 }
1074
1075 #[must_use]
1076 pub const fn exec_error_incompatible_persisted_format(&self) -> u64 {
1077 self.exec_error_incompatible_persisted_format
1078 }
1079
1080 #[must_use]
1081 pub const fn exec_error_not_found(&self) -> u64 {
1082 self.exec_error_not_found
1083 }
1084
1085 #[must_use]
1086 pub const fn exec_error_internal(&self) -> u64 {
1087 self.exec_error_internal
1088 }
1089
1090 #[must_use]
1091 pub const fn exec_error_conflict(&self) -> u64 {
1092 self.exec_error_conflict
1093 }
1094
1095 #[must_use]
1096 pub const fn exec_error_unsupported(&self) -> u64 {
1097 self.exec_error_unsupported
1098 }
1099
1100 #[must_use]
1101 pub const fn exec_error_invariant_violation(&self) -> u64 {
1102 self.exec_error_invariant_violation
1103 }
1104
1105 #[must_use]
1106 pub const fn exec_aborted(&self) -> u64 {
1107 self.exec_aborted
1108 }
1109
1110 #[must_use]
1111 pub const fn cache_shared_query_plan_hits(&self) -> u64 {
1112 self.cache_shared_query_plan_hits
1113 }
1114
1115 #[must_use]
1116 pub const fn cache_shared_query_plan_misses(&self) -> u64 {
1117 self.cache_shared_query_plan_misses
1118 }
1119
1120 #[must_use]
1121 pub const fn cache_shared_query_plan_inserts(&self) -> u64 {
1122 self.cache_shared_query_plan_inserts
1123 }
1124
1125 #[must_use]
1126 pub const fn cache_sql_compiled_command_hits(&self) -> u64 {
1127 self.cache_sql_compiled_command_hits
1128 }
1129
1130 #[must_use]
1131 pub const fn cache_sql_compiled_command_misses(&self) -> u64 {
1132 self.cache_sql_compiled_command_misses
1133 }
1134
1135 #[must_use]
1136 pub const fn cache_sql_compiled_command_inserts(&self) -> u64 {
1137 self.cache_sql_compiled_command_inserts
1138 }
1139
1140 #[must_use]
1141 pub const fn plan_index(&self) -> u64 {
1142 self.plan_index
1143 }
1144
1145 #[must_use]
1146 pub const fn plan_keys(&self) -> u64 {
1147 self.plan_keys
1148 }
1149
1150 #[must_use]
1151 pub const fn plan_range(&self) -> u64 {
1152 self.plan_range
1153 }
1154
1155 #[must_use]
1156 pub const fn plan_full_scan(&self) -> u64 {
1157 self.plan_full_scan
1158 }
1159
1160 #[must_use]
1161 pub const fn plan_by_key(&self) -> u64 {
1162 self.plan_by_key
1163 }
1164
1165 #[must_use]
1166 pub const fn plan_by_keys(&self) -> u64 {
1167 self.plan_by_keys
1168 }
1169
1170 #[must_use]
1171 pub const fn plan_key_range(&self) -> u64 {
1172 self.plan_key_range
1173 }
1174
1175 #[must_use]
1176 pub const fn plan_index_prefix(&self) -> u64 {
1177 self.plan_index_prefix
1178 }
1179
1180 #[must_use]
1181 pub const fn plan_index_multi_lookup(&self) -> u64 {
1182 self.plan_index_multi_lookup
1183 }
1184
1185 #[must_use]
1186 pub const fn plan_index_range(&self) -> u64 {
1187 self.plan_index_range
1188 }
1189
1190 #[must_use]
1191 pub const fn plan_explicit_full_scan(&self) -> u64 {
1192 self.plan_explicit_full_scan
1193 }
1194
1195 #[must_use]
1196 pub const fn plan_union(&self) -> u64 {
1197 self.plan_union
1198 }
1199
1200 #[must_use]
1201 pub const fn plan_intersection(&self) -> u64 {
1202 self.plan_intersection
1203 }
1204
1205 #[must_use]
1206 pub const fn plan_grouped_hash_materialized(&self) -> u64 {
1207 self.plan_grouped_hash_materialized
1208 }
1209
1210 #[must_use]
1211 pub const fn plan_grouped_ordered_materialized(&self) -> u64 {
1212 self.plan_grouped_ordered_materialized
1213 }
1214
1215 #[must_use]
1216 pub const fn rows_loaded(&self) -> u64 {
1217 self.rows_loaded
1218 }
1219
1220 #[must_use]
1221 pub const fn rows_saved(&self) -> u64 {
1222 self.rows_saved
1223 }
1224
1225 #[must_use]
1226 pub const fn rows_inserted(&self) -> u64 {
1227 self.rows_inserted
1228 }
1229
1230 #[must_use]
1231 pub const fn rows_updated(&self) -> u64 {
1232 self.rows_updated
1233 }
1234
1235 #[must_use]
1236 pub const fn rows_replaced(&self) -> u64 {
1237 self.rows_replaced
1238 }
1239
1240 #[must_use]
1241 pub const fn rows_scanned(&self) -> u64 {
1242 self.rows_scanned
1243 }
1244
1245 #[must_use]
1246 pub const fn rows_filtered(&self) -> u64 {
1247 self.rows_filtered
1248 }
1249
1250 #[must_use]
1251 pub const fn rows_aggregated(&self) -> u64 {
1252 self.rows_aggregated
1253 }
1254
1255 #[must_use]
1256 pub const fn rows_emitted(&self) -> u64 {
1257 self.rows_emitted
1258 }
1259
1260 #[must_use]
1261 pub const fn load_candidate_rows_scanned(&self) -> u64 {
1262 self.load_candidate_rows_scanned
1263 }
1264
1265 #[must_use]
1266 pub const fn load_candidate_rows_filtered(&self) -> u64 {
1267 self.load_candidate_rows_filtered
1268 }
1269
1270 #[must_use]
1271 pub const fn load_result_rows_emitted(&self) -> u64 {
1272 self.load_result_rows_emitted
1273 }
1274
1275 #[must_use]
1276 pub const fn rows_deleted(&self) -> u64 {
1277 self.rows_deleted
1278 }
1279
1280 #[must_use]
1281 pub const fn sql_insert_calls(&self) -> u64 {
1282 self.sql_insert_calls
1283 }
1284
1285 #[must_use]
1286 pub const fn sql_insert_select_calls(&self) -> u64 {
1287 self.sql_insert_select_calls
1288 }
1289
1290 #[must_use]
1291 pub const fn sql_update_calls(&self) -> u64 {
1292 self.sql_update_calls
1293 }
1294
1295 #[must_use]
1296 pub const fn sql_delete_calls(&self) -> u64 {
1297 self.sql_delete_calls
1298 }
1299
1300 #[must_use]
1301 pub const fn sql_write_matched_rows(&self) -> u64 {
1302 self.sql_write_matched_rows
1303 }
1304
1305 #[must_use]
1306 pub const fn sql_write_mutated_rows(&self) -> u64 {
1307 self.sql_write_mutated_rows
1308 }
1309
1310 #[must_use]
1311 pub const fn sql_write_returning_rows(&self) -> u64 {
1312 self.sql_write_returning_rows
1313 }
1314
1315 #[must_use]
1316 pub const fn sql_write_error_insert(&self) -> u64 {
1317 self.sql_write_error_insert
1318 }
1319
1320 #[must_use]
1321 pub const fn sql_write_error_insert_select(&self) -> u64 {
1322 self.sql_write_error_insert_select
1323 }
1324
1325 #[must_use]
1326 pub const fn sql_write_error_update(&self) -> u64 {
1327 self.sql_write_error_update
1328 }
1329
1330 #[must_use]
1331 pub const fn sql_write_error_delete(&self) -> u64 {
1332 self.sql_write_error_delete
1333 }
1334
1335 #[must_use]
1336 pub const fn sql_write_error_corruption(&self) -> u64 {
1337 self.sql_write_error_corruption
1338 }
1339
1340 #[must_use]
1341 pub const fn sql_write_error_incompatible_persisted_format(&self) -> u64 {
1342 self.sql_write_error_incompatible_persisted_format
1343 }
1344
1345 #[must_use]
1346 pub const fn sql_write_error_not_found(&self) -> u64 {
1347 self.sql_write_error_not_found
1348 }
1349
1350 #[must_use]
1351 pub const fn sql_write_error_internal(&self) -> u64 {
1352 self.sql_write_error_internal
1353 }
1354
1355 #[must_use]
1356 pub const fn sql_write_error_conflict(&self) -> u64 {
1357 self.sql_write_error_conflict
1358 }
1359
1360 #[must_use]
1361 pub const fn sql_write_error_unsupported(&self) -> u64 {
1362 self.sql_write_error_unsupported
1363 }
1364
1365 #[must_use]
1366 pub const fn sql_write_error_invariant_violation(&self) -> u64 {
1367 self.sql_write_error_invariant_violation
1368 }
1369
1370 #[must_use]
1371 pub const fn index_inserts(&self) -> u64 {
1372 self.index_inserts
1373 }
1374
1375 #[must_use]
1376 pub const fn index_removes(&self) -> u64 {
1377 self.index_removes
1378 }
1379
1380 #[must_use]
1381 pub const fn reverse_index_inserts(&self) -> u64 {
1382 self.reverse_index_inserts
1383 }
1384
1385 #[must_use]
1386 pub const fn reverse_index_removes(&self) -> u64 {
1387 self.reverse_index_removes
1388 }
1389
1390 #[must_use]
1391 pub const fn relation_reverse_lookups(&self) -> u64 {
1392 self.relation_reverse_lookups
1393 }
1394
1395 #[must_use]
1396 pub const fn relation_delete_blocks(&self) -> u64 {
1397 self.relation_delete_blocks
1398 }
1399
1400 #[must_use]
1401 pub const fn write_rows_touched(&self) -> u64 {
1402 self.write_rows_touched
1403 }
1404
1405 #[must_use]
1406 pub const fn write_index_entries_changed(&self) -> u64 {
1407 self.write_index_entries_changed
1408 }
1409
1410 #[must_use]
1411 pub const fn write_reverse_index_entries_changed(&self) -> u64 {
1412 self.write_reverse_index_entries_changed
1413 }
1414
1415 #[must_use]
1416 pub const fn write_relation_checks(&self) -> u64 {
1417 self.write_relation_checks
1418 }
1419
1420 #[must_use]
1421 pub const fn unique_violations(&self) -> u64 {
1422 self.unique_violations
1423 }
1424
1425 #[must_use]
1426 pub const fn non_atomic_partial_commits(&self) -> u64 {
1427 self.non_atomic_partial_commits
1428 }
1429
1430 #[must_use]
1431 pub const fn non_atomic_partial_rows_committed(&self) -> u64 {
1432 self.non_atomic_partial_rows_committed
1433 }
1434
1435 #[must_use]
1437 pub const fn load_selectivity_ratio(&self) -> Option<MetricRatio> {
1438 ratio(
1439 self.load_result_rows_emitted,
1440 self.load_candidate_rows_scanned,
1441 )
1442 }
1443
1444 #[must_use]
1446 pub const fn load_filter_ratio(&self) -> Option<MetricRatio> {
1447 ratio(
1448 self.load_candidate_rows_filtered,
1449 self.load_candidate_rows_scanned,
1450 )
1451 }
1452
1453 #[must_use]
1455 pub const fn sql_write_mutation_ratio(&self) -> Option<MetricRatio> {
1456 ratio(self.sql_write_mutated_rows, self.sql_write_matched_rows)
1457 }
1458
1459 #[must_use]
1461 pub const fn sql_write_returning_ratio(&self) -> Option<MetricRatio> {
1462 ratio(self.sql_write_returning_rows, self.sql_write_mutated_rows)
1463 }
1464
1465 #[must_use]
1467 pub const fn write_index_entries_per_row(&self) -> Option<MetricRatio> {
1468 ratio(self.write_index_entries_changed, self.write_rows_touched)
1469 }
1470
1471 #[must_use]
1473 pub const fn write_reverse_index_entries_per_row(&self) -> Option<MetricRatio> {
1474 ratio(
1475 self.write_reverse_index_entries_changed,
1476 self.write_rows_touched,
1477 )
1478 }
1479
1480 #[must_use]
1482 pub const fn write_relation_checks_per_row(&self) -> Option<MetricRatio> {
1483 ratio(self.write_relation_checks, self.write_rows_touched)
1484 }
1485
1486 const fn activity_score(&self) -> u64 {
1489 self.load_calls
1490 .saturating_add(self.save_calls)
1491 .saturating_add(self.delete_calls)
1492 .saturating_add(self.save_insert_calls)
1493 .saturating_add(self.save_update_calls)
1494 .saturating_add(self.save_replace_calls)
1495 .saturating_add(self.exec_success)
1496 .saturating_add(self.exec_error_corruption)
1497 .saturating_add(self.exec_error_incompatible_persisted_format)
1498 .saturating_add(self.exec_error_not_found)
1499 .saturating_add(self.exec_error_internal)
1500 .saturating_add(self.exec_error_conflict)
1501 .saturating_add(self.exec_error_unsupported)
1502 .saturating_add(self.exec_error_invariant_violation)
1503 .saturating_add(self.exec_aborted)
1504 .saturating_add(self.cache_shared_query_plan_hits)
1505 .saturating_add(self.cache_shared_query_plan_misses)
1506 .saturating_add(self.cache_shared_query_plan_inserts)
1507 .saturating_add(self.cache_sql_compiled_command_hits)
1508 .saturating_add(self.cache_sql_compiled_command_misses)
1509 .saturating_add(self.cache_sql_compiled_command_inserts)
1510 .saturating_add(self.plan_index)
1511 .saturating_add(self.plan_keys)
1512 .saturating_add(self.plan_range)
1513 .saturating_add(self.plan_full_scan)
1514 .saturating_add(self.plan_by_key)
1515 .saturating_add(self.plan_by_keys)
1516 .saturating_add(self.plan_key_range)
1517 .saturating_add(self.plan_index_prefix)
1518 .saturating_add(self.plan_index_multi_lookup)
1519 .saturating_add(self.plan_index_range)
1520 .saturating_add(self.plan_explicit_full_scan)
1521 .saturating_add(self.plan_union)
1522 .saturating_add(self.plan_intersection)
1523 .saturating_add(self.plan_grouped_hash_materialized)
1524 .saturating_add(self.plan_grouped_ordered_materialized)
1525 .saturating_add(self.rows_loaded)
1526 .saturating_add(self.rows_saved)
1527 .saturating_add(self.rows_inserted)
1528 .saturating_add(self.rows_updated)
1529 .saturating_add(self.rows_replaced)
1530 .saturating_add(self.rows_scanned)
1531 .saturating_add(self.rows_filtered)
1532 .saturating_add(self.rows_aggregated)
1533 .saturating_add(self.rows_emitted)
1534 .saturating_add(self.load_candidate_rows_scanned)
1535 .saturating_add(self.load_candidate_rows_filtered)
1536 .saturating_add(self.load_result_rows_emitted)
1537 .saturating_add(self.rows_deleted)
1538 .saturating_add(self.sql_insert_calls)
1539 .saturating_add(self.sql_insert_select_calls)
1540 .saturating_add(self.sql_update_calls)
1541 .saturating_add(self.sql_delete_calls)
1542 .saturating_add(self.sql_write_matched_rows)
1543 .saturating_add(self.sql_write_mutated_rows)
1544 .saturating_add(self.sql_write_returning_rows)
1545 .saturating_add(self.sql_write_error_insert)
1546 .saturating_add(self.sql_write_error_insert_select)
1547 .saturating_add(self.sql_write_error_update)
1548 .saturating_add(self.sql_write_error_delete)
1549 .saturating_add(self.sql_write_error_corruption)
1550 .saturating_add(self.sql_write_error_incompatible_persisted_format)
1551 .saturating_add(self.sql_write_error_not_found)
1552 .saturating_add(self.sql_write_error_internal)
1553 .saturating_add(self.sql_write_error_conflict)
1554 .saturating_add(self.sql_write_error_unsupported)
1555 .saturating_add(self.sql_write_error_invariant_violation)
1556 .saturating_add(self.index_inserts)
1557 .saturating_add(self.index_removes)
1558 .saturating_add(self.reverse_index_inserts)
1559 .saturating_add(self.reverse_index_removes)
1560 .saturating_add(self.relation_reverse_lookups)
1561 .saturating_add(self.relation_delete_blocks)
1562 .saturating_add(self.write_rows_touched)
1563 .saturating_add(self.write_index_entries_changed)
1564 .saturating_add(self.write_reverse_index_entries_changed)
1565 .saturating_add(self.write_relation_checks)
1566 .saturating_add(self.unique_violations)
1567 .saturating_add(self.non_atomic_partial_commits)
1568 .saturating_add(self.non_atomic_partial_rows_committed)
1569 }
1570}
1571
1572fn entity_summary_from_counters(path: &str, ops: &EntityCounters) -> EntitySummary {
1577 EntitySummary {
1578 path: path.to_string(),
1579 load_calls: ops.load_calls,
1580 save_calls: ops.save_calls,
1581 delete_calls: ops.delete_calls,
1582 save_insert_calls: ops.save_insert_calls,
1583 save_update_calls: ops.save_update_calls,
1584 save_replace_calls: ops.save_replace_calls,
1585 exec_success: ops.exec_success,
1586 exec_error_corruption: ops.exec_error_corruption,
1587 exec_error_incompatible_persisted_format: ops.exec_error_incompatible_persisted_format,
1588 exec_error_not_found: ops.exec_error_not_found,
1589 exec_error_internal: ops.exec_error_internal,
1590 exec_error_conflict: ops.exec_error_conflict,
1591 exec_error_unsupported: ops.exec_error_unsupported,
1592 exec_error_invariant_violation: ops.exec_error_invariant_violation,
1593 exec_aborted: ops.exec_aborted,
1594 cache_shared_query_plan_hits: ops.cache_shared_query_plan_hits,
1595 cache_shared_query_plan_misses: ops.cache_shared_query_plan_misses,
1596 cache_shared_query_plan_inserts: ops.cache_shared_query_plan_inserts,
1597 cache_sql_compiled_command_hits: ops.cache_sql_compiled_command_hits,
1598 cache_sql_compiled_command_misses: ops.cache_sql_compiled_command_misses,
1599 cache_sql_compiled_command_inserts: ops.cache_sql_compiled_command_inserts,
1600 plan_index: ops.plan_index,
1601 plan_keys: ops.plan_keys,
1602 plan_range: ops.plan_range,
1603 plan_full_scan: ops.plan_full_scan,
1604 plan_by_key: ops.plan_by_key,
1605 plan_by_keys: ops.plan_by_keys,
1606 plan_key_range: ops.plan_key_range,
1607 plan_index_prefix: ops.plan_index_prefix,
1608 plan_index_multi_lookup: ops.plan_index_multi_lookup,
1609 plan_index_range: ops.plan_index_range,
1610 plan_explicit_full_scan: ops.plan_explicit_full_scan,
1611 plan_union: ops.plan_union,
1612 plan_intersection: ops.plan_intersection,
1613 plan_grouped_hash_materialized: ops.plan_grouped_hash_materialized,
1614 plan_grouped_ordered_materialized: ops.plan_grouped_ordered_materialized,
1615 rows_loaded: ops.rows_loaded,
1616 rows_saved: ops.rows_saved,
1617 rows_inserted: ops.rows_inserted,
1618 rows_updated: ops.rows_updated,
1619 rows_replaced: ops.rows_replaced,
1620 rows_scanned: ops.rows_scanned,
1621 rows_filtered: ops.rows_filtered,
1622 rows_aggregated: ops.rows_aggregated,
1623 rows_emitted: ops.rows_emitted,
1624 load_candidate_rows_scanned: ops.load_candidate_rows_scanned,
1625 load_candidate_rows_filtered: ops.load_candidate_rows_filtered,
1626 load_result_rows_emitted: ops.load_result_rows_emitted,
1627 rows_deleted: ops.rows_deleted,
1628 sql_insert_calls: ops.sql_insert_calls,
1629 sql_insert_select_calls: ops.sql_insert_select_calls,
1630 sql_update_calls: ops.sql_update_calls,
1631 sql_delete_calls: ops.sql_delete_calls,
1632 sql_write_matched_rows: ops.sql_write_matched_rows,
1633 sql_write_mutated_rows: ops.sql_write_mutated_rows,
1634 sql_write_returning_rows: ops.sql_write_returning_rows,
1635 sql_write_error_insert: ops.sql_write_error_insert,
1636 sql_write_error_insert_select: ops.sql_write_error_insert_select,
1637 sql_write_error_update: ops.sql_write_error_update,
1638 sql_write_error_delete: ops.sql_write_error_delete,
1639 sql_write_error_corruption: ops.sql_write_error_corruption,
1640 sql_write_error_incompatible_persisted_format: ops
1641 .sql_write_error_incompatible_persisted_format,
1642 sql_write_error_not_found: ops.sql_write_error_not_found,
1643 sql_write_error_internal: ops.sql_write_error_internal,
1644 sql_write_error_conflict: ops.sql_write_error_conflict,
1645 sql_write_error_unsupported: ops.sql_write_error_unsupported,
1646 sql_write_error_invariant_violation: ops.sql_write_error_invariant_violation,
1647 index_inserts: ops.index_inserts,
1648 index_removes: ops.index_removes,
1649 reverse_index_inserts: ops.reverse_index_inserts,
1650 reverse_index_removes: ops.reverse_index_removes,
1651 relation_reverse_lookups: ops.relation_reverse_lookups,
1652 relation_delete_blocks: ops.relation_delete_blocks,
1653 write_rows_touched: ops.write_rows_touched,
1654 write_index_entries_changed: ops.write_index_entries_changed,
1655 write_reverse_index_entries_changed: ops.write_reverse_index_entries_changed,
1656 write_relation_checks: ops.write_relation_checks,
1657 unique_violations: ops.unique_violations,
1658 non_atomic_partial_commits: ops.non_atomic_partial_commits,
1659 non_atomic_partial_rows_committed: ops.non_atomic_partial_rows_committed,
1660 }
1661}
1662
1663#[must_use]
1673pub(super) fn report_window_start(window_start_ms: Option<u64>) -> EventReport {
1674 let snap = with_state(Clone::clone);
1675 if let Some(requested_window_start_ms) = window_start_ms
1676 && requested_window_start_ms > snap.window_start_ms
1677 {
1678 return EventReport::new(
1679 None,
1680 Vec::new(),
1681 false,
1682 window_start_ms,
1683 snap.window_start_ms,
1684 );
1685 }
1686
1687 let mut entity_counters: Vec<EntitySummary> = Vec::new();
1688 for (path, ops) in &snap.entities {
1689 entity_counters.push(entity_summary_from_counters(path, ops));
1690 }
1691
1692 entity_counters.sort_by(|a, b| {
1693 b.activity_score()
1694 .cmp(&a.activity_score())
1695 .then_with(|| b.rows_loaded.cmp(&a.rows_loaded))
1696 .then_with(|| b.rows_saved.cmp(&a.rows_saved))
1697 .then_with(|| b.rows_scanned.cmp(&a.rows_scanned))
1698 .then_with(|| b.rows_deleted.cmp(&a.rows_deleted))
1699 .then_with(|| a.path.cmp(&b.path))
1700 });
1701
1702 EventReport::new(
1703 Some(EventCounters::new(
1704 snap.ops.clone(),
1705 snap.perf.clone(),
1706 snap.window_start_ms,
1707 now_millis(),
1708 )),
1709 entity_counters,
1710 true,
1711 window_start_ms,
1712 snap.window_start_ms,
1713 )
1714}