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#[cfg_attr(doc, doc = "EventOps\n\nOperation counters.")]
35#[derive(CandidType, Clone, Debug, Default, Deserialize)]
36pub struct EventOps {
37 pub(crate) load_calls: u64,
39 pub(crate) save_calls: u64,
40 pub(crate) delete_calls: u64,
41 pub(crate) save_insert_calls: u64,
42 pub(crate) save_update_calls: u64,
43 pub(crate) save_replace_calls: u64,
44 pub(crate) exec_success: u64,
45 pub(crate) exec_error_corruption: u64,
46 pub(crate) exec_error_incompatible_persisted_format: u64,
47 pub(crate) exec_error_not_found: u64,
48 pub(crate) exec_error_internal: u64,
49 pub(crate) exec_error_conflict: u64,
50 pub(crate) exec_error_unsupported: u64,
51 pub(crate) exec_error_invariant_violation: u64,
52 pub(crate) exec_aborted: u64,
53
54 pub(crate) plan_index: u64,
56 pub(crate) plan_keys: u64,
57 pub(crate) plan_range: u64,
58 pub(crate) plan_full_scan: u64,
59 pub(crate) plan_by_key: u64,
60 pub(crate) plan_by_keys: u64,
61 pub(crate) plan_key_range: u64,
62 pub(crate) plan_index_prefix: u64,
63 pub(crate) plan_index_multi_lookup: u64,
64 pub(crate) plan_index_range: u64,
65 pub(crate) plan_explicit_full_scan: u64,
66 pub(crate) plan_union: u64,
67 pub(crate) plan_intersection: u64,
68 pub(crate) plan_grouped_hash_materialized: u64,
69 pub(crate) plan_grouped_ordered_materialized: u64,
70
71 pub(crate) rows_loaded: u64,
73 pub(crate) rows_saved: u64,
74 pub(crate) rows_inserted: u64,
75 pub(crate) rows_updated: u64,
76 pub(crate) rows_replaced: u64,
77 pub(crate) rows_scanned: u64,
78 pub(crate) rows_filtered: u64,
79 pub(crate) rows_aggregated: u64,
80 pub(crate) rows_emitted: u64,
81 pub(crate) rows_deleted: u64,
82
83 pub(crate) index_inserts: u64,
85 pub(crate) index_removes: u64,
86 pub(crate) reverse_index_inserts: u64,
87 pub(crate) reverse_index_removes: u64,
88 pub(crate) relation_reverse_lookups: u64,
89 pub(crate) relation_delete_blocks: u64,
90 pub(crate) unique_violations: u64,
91 pub(crate) non_atomic_partial_commits: u64,
92 pub(crate) non_atomic_partial_rows_committed: u64,
93}
94
95impl EventOps {
96 #[must_use]
97 pub const fn load_calls(&self) -> u64 {
98 self.load_calls
99 }
100
101 #[must_use]
102 pub const fn save_calls(&self) -> u64 {
103 self.save_calls
104 }
105
106 #[must_use]
107 pub const fn delete_calls(&self) -> u64 {
108 self.delete_calls
109 }
110
111 #[must_use]
112 pub const fn save_insert_calls(&self) -> u64 {
113 self.save_insert_calls
114 }
115
116 #[must_use]
117 pub const fn save_update_calls(&self) -> u64 {
118 self.save_update_calls
119 }
120
121 #[must_use]
122 pub const fn save_replace_calls(&self) -> u64 {
123 self.save_replace_calls
124 }
125
126 #[must_use]
127 pub const fn exec_success(&self) -> u64 {
128 self.exec_success
129 }
130
131 #[must_use]
132 pub const fn exec_error_corruption(&self) -> u64 {
133 self.exec_error_corruption
134 }
135
136 #[must_use]
137 pub const fn exec_error_incompatible_persisted_format(&self) -> u64 {
138 self.exec_error_incompatible_persisted_format
139 }
140
141 #[must_use]
142 pub const fn exec_error_not_found(&self) -> u64 {
143 self.exec_error_not_found
144 }
145
146 #[must_use]
147 pub const fn exec_error_internal(&self) -> u64 {
148 self.exec_error_internal
149 }
150
151 #[must_use]
152 pub const fn exec_error_conflict(&self) -> u64 {
153 self.exec_error_conflict
154 }
155
156 #[must_use]
157 pub const fn exec_error_unsupported(&self) -> u64 {
158 self.exec_error_unsupported
159 }
160
161 #[must_use]
162 pub const fn exec_error_invariant_violation(&self) -> u64 {
163 self.exec_error_invariant_violation
164 }
165
166 #[must_use]
167 pub const fn exec_aborted(&self) -> u64 {
168 self.exec_aborted
169 }
170
171 #[must_use]
172 pub const fn plan_index(&self) -> u64 {
173 self.plan_index
174 }
175
176 #[must_use]
177 pub const fn plan_keys(&self) -> u64 {
178 self.plan_keys
179 }
180
181 #[must_use]
182 pub const fn plan_range(&self) -> u64 {
183 self.plan_range
184 }
185
186 #[must_use]
187 pub const fn plan_full_scan(&self) -> u64 {
188 self.plan_full_scan
189 }
190
191 #[must_use]
192 pub const fn plan_by_key(&self) -> u64 {
193 self.plan_by_key
194 }
195
196 #[must_use]
197 pub const fn plan_by_keys(&self) -> u64 {
198 self.plan_by_keys
199 }
200
201 #[must_use]
202 pub const fn plan_key_range(&self) -> u64 {
203 self.plan_key_range
204 }
205
206 #[must_use]
207 pub const fn plan_index_prefix(&self) -> u64 {
208 self.plan_index_prefix
209 }
210
211 #[must_use]
212 pub const fn plan_index_multi_lookup(&self) -> u64 {
213 self.plan_index_multi_lookup
214 }
215
216 #[must_use]
217 pub const fn plan_index_range(&self) -> u64 {
218 self.plan_index_range
219 }
220
221 #[must_use]
222 pub const fn plan_explicit_full_scan(&self) -> u64 {
223 self.plan_explicit_full_scan
224 }
225
226 #[must_use]
227 pub const fn plan_union(&self) -> u64 {
228 self.plan_union
229 }
230
231 #[must_use]
232 pub const fn plan_intersection(&self) -> u64 {
233 self.plan_intersection
234 }
235
236 #[must_use]
237 pub const fn plan_grouped_hash_materialized(&self) -> u64 {
238 self.plan_grouped_hash_materialized
239 }
240
241 #[must_use]
242 pub const fn plan_grouped_ordered_materialized(&self) -> u64 {
243 self.plan_grouped_ordered_materialized
244 }
245
246 #[must_use]
247 pub const fn rows_loaded(&self) -> u64 {
248 self.rows_loaded
249 }
250
251 #[must_use]
252 pub const fn rows_saved(&self) -> u64 {
253 self.rows_saved
254 }
255
256 #[must_use]
257 pub const fn rows_inserted(&self) -> u64 {
258 self.rows_inserted
259 }
260
261 #[must_use]
262 pub const fn rows_updated(&self) -> u64 {
263 self.rows_updated
264 }
265
266 #[must_use]
267 pub const fn rows_replaced(&self) -> u64 {
268 self.rows_replaced
269 }
270
271 #[must_use]
272 pub const fn rows_scanned(&self) -> u64 {
273 self.rows_scanned
274 }
275
276 #[must_use]
277 pub const fn rows_filtered(&self) -> u64 {
278 self.rows_filtered
279 }
280
281 #[must_use]
282 pub const fn rows_aggregated(&self) -> u64 {
283 self.rows_aggregated
284 }
285
286 #[must_use]
287 pub const fn rows_emitted(&self) -> u64 {
288 self.rows_emitted
289 }
290
291 #[must_use]
292 pub const fn rows_deleted(&self) -> u64 {
293 self.rows_deleted
294 }
295
296 #[must_use]
297 pub const fn index_inserts(&self) -> u64 {
298 self.index_inserts
299 }
300
301 #[must_use]
302 pub const fn index_removes(&self) -> u64 {
303 self.index_removes
304 }
305
306 #[must_use]
307 pub const fn reverse_index_inserts(&self) -> u64 {
308 self.reverse_index_inserts
309 }
310
311 #[must_use]
312 pub const fn reverse_index_removes(&self) -> u64 {
313 self.reverse_index_removes
314 }
315
316 #[must_use]
317 pub const fn relation_reverse_lookups(&self) -> u64 {
318 self.relation_reverse_lookups
319 }
320
321 #[must_use]
322 pub const fn relation_delete_blocks(&self) -> u64 {
323 self.relation_delete_blocks
324 }
325
326 #[must_use]
327 pub const fn unique_violations(&self) -> u64 {
328 self.unique_violations
329 }
330
331 #[must_use]
332 pub const fn non_atomic_partial_commits(&self) -> u64 {
333 self.non_atomic_partial_commits
334 }
335
336 #[must_use]
337 pub const fn non_atomic_partial_rows_committed(&self) -> u64 {
338 self.non_atomic_partial_rows_committed
339 }
340}
341
342#[derive(Clone, Debug, Default)]
343pub(crate) struct EntityCounters {
344 pub(crate) load_calls: u64,
345 pub(crate) save_calls: u64,
346 pub(crate) delete_calls: u64,
347 pub(crate) save_insert_calls: u64,
348 pub(crate) save_update_calls: u64,
349 pub(crate) save_replace_calls: u64,
350 pub(crate) exec_success: u64,
351 pub(crate) exec_error_corruption: u64,
352 pub(crate) exec_error_incompatible_persisted_format: u64,
353 pub(crate) exec_error_not_found: u64,
354 pub(crate) exec_error_internal: u64,
355 pub(crate) exec_error_conflict: u64,
356 pub(crate) exec_error_unsupported: u64,
357 pub(crate) exec_error_invariant_violation: u64,
358 pub(crate) exec_aborted: u64,
359 pub(crate) plan_index: u64,
360 pub(crate) plan_keys: u64,
361 pub(crate) plan_range: u64,
362 pub(crate) plan_full_scan: u64,
363 pub(crate) plan_by_key: u64,
364 pub(crate) plan_by_keys: u64,
365 pub(crate) plan_key_range: u64,
366 pub(crate) plan_index_prefix: u64,
367 pub(crate) plan_index_multi_lookup: u64,
368 pub(crate) plan_index_range: u64,
369 pub(crate) plan_explicit_full_scan: u64,
370 pub(crate) plan_union: u64,
371 pub(crate) plan_intersection: u64,
372 pub(crate) plan_grouped_hash_materialized: u64,
373 pub(crate) plan_grouped_ordered_materialized: u64,
374 pub(crate) rows_loaded: u64,
375 pub(crate) rows_saved: u64,
376 pub(crate) rows_inserted: u64,
377 pub(crate) rows_updated: u64,
378 pub(crate) rows_replaced: u64,
379 pub(crate) rows_scanned: u64,
380 pub(crate) rows_filtered: u64,
381 pub(crate) rows_aggregated: u64,
382 pub(crate) rows_emitted: u64,
383 pub(crate) rows_deleted: u64,
384 pub(crate) index_inserts: u64,
385 pub(crate) index_removes: u64,
386 pub(crate) reverse_index_inserts: u64,
387 pub(crate) reverse_index_removes: u64,
388 pub(crate) relation_reverse_lookups: u64,
389 pub(crate) relation_delete_blocks: u64,
390 pub(crate) unique_violations: u64,
391 pub(crate) non_atomic_partial_commits: u64,
392 pub(crate) non_atomic_partial_rows_committed: u64,
393}
394
395#[cfg_attr(doc, doc = "EventPerf\n\nInstruction totals and maxima.")]
396#[derive(CandidType, Clone, Debug, Default, Deserialize)]
397pub struct EventPerf {
398 pub(crate) load_inst_total: u128,
400 pub(crate) save_inst_total: u128,
401 pub(crate) delete_inst_total: u128,
402
403 pub(crate) load_inst_max: u64,
405 pub(crate) save_inst_max: u64,
406 pub(crate) delete_inst_max: u64,
407}
408
409impl EventPerf {
410 #[must_use]
411 pub const fn new(
412 load_inst_total: u128,
413 save_inst_total: u128,
414 delete_inst_total: u128,
415 load_inst_max: u64,
416 save_inst_max: u64,
417 delete_inst_max: u64,
418 ) -> Self {
419 Self {
420 load_inst_total,
421 save_inst_total,
422 delete_inst_total,
423 load_inst_max,
424 save_inst_max,
425 delete_inst_max,
426 }
427 }
428
429 #[must_use]
430 pub const fn load_inst_total(&self) -> u128 {
431 self.load_inst_total
432 }
433
434 #[must_use]
435 pub const fn save_inst_total(&self) -> u128 {
436 self.save_inst_total
437 }
438
439 #[must_use]
440 pub const fn delete_inst_total(&self) -> u128 {
441 self.delete_inst_total
442 }
443
444 #[must_use]
445 pub const fn load_inst_max(&self) -> u64 {
446 self.load_inst_max
447 }
448
449 #[must_use]
450 pub const fn save_inst_max(&self) -> u64 {
451 self.save_inst_max
452 }
453
454 #[must_use]
455 pub const fn delete_inst_max(&self) -> u64 {
456 self.delete_inst_max
457 }
458}
459
460thread_local! {
461 static EVENT_STATE: RefCell<EventState> = RefCell::new(EventState::default());
462}
463
464pub(crate) fn with_state<R>(f: impl FnOnce(&EventState) -> R) -> R {
466 EVENT_STATE.with(|m| f(&m.borrow()))
467}
468
469pub(crate) fn with_state_mut<R>(f: impl FnOnce(&mut EventState) -> R) -> R {
471 EVENT_STATE.with(|m| f(&mut m.borrow_mut()))
472}
473
474pub(super) fn reset() {
476 with_state_mut(|m| *m = EventState::default());
477}
478
479pub(crate) fn reset_all() {
481 reset();
482}
483
484pub(super) fn add_instructions(total: &mut u128, max: &mut u64, delta_inst: u64) {
486 *total = total.saturating_add(u128::from(delta_inst));
487 if delta_inst > *max {
488 *max = delta_inst;
489 }
490}
491
492#[cfg_attr(doc, doc = "EventReport\n\nMetrics query payload.")]
493#[derive(CandidType, Clone, Debug, Default, Deserialize)]
494pub struct EventReport {
495 counters: Option<EventCounters>,
496 entity_counters: Vec<EntitySummary>,
497 window_filter_matched: bool,
498 requested_window_start_ms: Option<u64>,
499 active_window_start_ms: u64,
500}
501
502impl EventReport {
503 #[must_use]
504 pub(crate) const fn new(
505 counters: Option<EventCounters>,
506 entity_counters: Vec<EntitySummary>,
507 window_filter_matched: bool,
508 requested_window_start_ms: Option<u64>,
509 active_window_start_ms: u64,
510 ) -> Self {
511 Self {
512 counters,
513 entity_counters,
514 window_filter_matched,
515 requested_window_start_ms,
516 active_window_start_ms,
517 }
518 }
519
520 #[must_use]
521 pub const fn counters(&self) -> Option<&EventCounters> {
522 self.counters.as_ref()
523 }
524
525 #[must_use]
526 pub fn entity_counters(&self) -> &[EntitySummary] {
527 &self.entity_counters
528 }
529
530 #[must_use]
531 pub const fn window_filter_matched(&self) -> bool {
532 self.window_filter_matched
533 }
534
535 #[must_use]
536 pub const fn requested_window_start_ms(&self) -> Option<u64> {
537 self.requested_window_start_ms
538 }
539
540 #[must_use]
541 pub const fn active_window_start_ms(&self) -> u64 {
542 self.active_window_start_ms
543 }
544
545 #[must_use]
546 pub fn into_counters(self) -> Option<EventCounters> {
547 self.counters
548 }
549
550 #[must_use]
551 pub fn into_entity_counters(self) -> Vec<EntitySummary> {
552 self.entity_counters
553 }
554}
555
556#[derive(CandidType, Clone, Debug, Default, Deserialize)]
565pub struct EventCounters {
566 pub(crate) ops: EventOps,
567 pub(crate) perf: EventPerf,
568 pub(crate) window_start_ms: u64,
569 pub(crate) window_end_ms: u64,
570 pub(crate) window_duration_ms: u64,
571}
572
573impl EventCounters {
574 #[must_use]
575 pub(crate) const fn new(
576 ops: EventOps,
577 perf: EventPerf,
578 window_start_ms: u64,
579 window_end_ms: u64,
580 ) -> Self {
581 Self {
582 ops,
583 perf,
584 window_start_ms,
585 window_end_ms,
586 window_duration_ms: window_end_ms.saturating_sub(window_start_ms),
587 }
588 }
589
590 #[must_use]
591 pub const fn ops(&self) -> &EventOps {
592 &self.ops
593 }
594
595 #[must_use]
596 pub const fn perf(&self) -> &EventPerf {
597 &self.perf
598 }
599
600 #[must_use]
601 pub const fn window_start_ms(&self) -> u64 {
602 self.window_start_ms
603 }
604
605 #[must_use]
606 pub const fn window_end_ms(&self) -> u64 {
607 self.window_end_ms
608 }
609
610 #[must_use]
611 pub const fn window_duration_ms(&self) -> u64 {
612 self.window_duration_ms
613 }
614}
615
616#[cfg_attr(doc, doc = "EntitySummary\n\nPer-entity metrics summary.")]
617#[derive(CandidType, Clone, Debug, Default, Deserialize)]
618pub struct EntitySummary {
619 path: String,
620 load_calls: u64,
621 save_calls: u64,
622 delete_calls: u64,
623 save_insert_calls: u64,
624 save_update_calls: u64,
625 save_replace_calls: u64,
626 exec_success: u64,
627 exec_error_corruption: u64,
628 exec_error_incompatible_persisted_format: u64,
629 exec_error_not_found: u64,
630 exec_error_internal: u64,
631 exec_error_conflict: u64,
632 exec_error_unsupported: u64,
633 exec_error_invariant_violation: u64,
634 exec_aborted: u64,
635 plan_index: u64,
636 plan_keys: u64,
637 plan_range: u64,
638 plan_full_scan: u64,
639 plan_by_key: u64,
640 plan_by_keys: u64,
641 plan_key_range: u64,
642 plan_index_prefix: u64,
643 plan_index_multi_lookup: u64,
644 plan_index_range: u64,
645 plan_explicit_full_scan: u64,
646 plan_union: u64,
647 plan_intersection: u64,
648 plan_grouped_hash_materialized: u64,
649 plan_grouped_ordered_materialized: u64,
650 rows_loaded: u64,
651 rows_saved: u64,
652 rows_inserted: u64,
653 rows_updated: u64,
654 rows_replaced: u64,
655 rows_scanned: u64,
656 rows_filtered: u64,
657 rows_aggregated: u64,
658 rows_emitted: u64,
659 rows_deleted: u64,
660 index_inserts: u64,
661 index_removes: u64,
662 reverse_index_inserts: u64,
663 reverse_index_removes: u64,
664 relation_reverse_lookups: u64,
665 relation_delete_blocks: u64,
666 unique_violations: u64,
667 non_atomic_partial_commits: u64,
668 non_atomic_partial_rows_committed: u64,
669}
670
671impl EntitySummary {
672 #[must_use]
673 pub const fn path(&self) -> &str {
674 self.path.as_str()
675 }
676
677 #[must_use]
678 pub const fn load_calls(&self) -> u64 {
679 self.load_calls
680 }
681
682 #[must_use]
683 pub const fn save_calls(&self) -> u64 {
684 self.save_calls
685 }
686
687 #[must_use]
688 pub const fn delete_calls(&self) -> u64 {
689 self.delete_calls
690 }
691
692 #[must_use]
693 pub const fn save_insert_calls(&self) -> u64 {
694 self.save_insert_calls
695 }
696
697 #[must_use]
698 pub const fn save_update_calls(&self) -> u64 {
699 self.save_update_calls
700 }
701
702 #[must_use]
703 pub const fn save_replace_calls(&self) -> u64 {
704 self.save_replace_calls
705 }
706
707 #[must_use]
708 pub const fn exec_success(&self) -> u64 {
709 self.exec_success
710 }
711
712 #[must_use]
713 pub const fn exec_error_corruption(&self) -> u64 {
714 self.exec_error_corruption
715 }
716
717 #[must_use]
718 pub const fn exec_error_incompatible_persisted_format(&self) -> u64 {
719 self.exec_error_incompatible_persisted_format
720 }
721
722 #[must_use]
723 pub const fn exec_error_not_found(&self) -> u64 {
724 self.exec_error_not_found
725 }
726
727 #[must_use]
728 pub const fn exec_error_internal(&self) -> u64 {
729 self.exec_error_internal
730 }
731
732 #[must_use]
733 pub const fn exec_error_conflict(&self) -> u64 {
734 self.exec_error_conflict
735 }
736
737 #[must_use]
738 pub const fn exec_error_unsupported(&self) -> u64 {
739 self.exec_error_unsupported
740 }
741
742 #[must_use]
743 pub const fn exec_error_invariant_violation(&self) -> u64 {
744 self.exec_error_invariant_violation
745 }
746
747 #[must_use]
748 pub const fn exec_aborted(&self) -> u64 {
749 self.exec_aborted
750 }
751
752 #[must_use]
753 pub const fn plan_index(&self) -> u64 {
754 self.plan_index
755 }
756
757 #[must_use]
758 pub const fn plan_keys(&self) -> u64 {
759 self.plan_keys
760 }
761
762 #[must_use]
763 pub const fn plan_range(&self) -> u64 {
764 self.plan_range
765 }
766
767 #[must_use]
768 pub const fn plan_full_scan(&self) -> u64 {
769 self.plan_full_scan
770 }
771
772 #[must_use]
773 pub const fn plan_by_key(&self) -> u64 {
774 self.plan_by_key
775 }
776
777 #[must_use]
778 pub const fn plan_by_keys(&self) -> u64 {
779 self.plan_by_keys
780 }
781
782 #[must_use]
783 pub const fn plan_key_range(&self) -> u64 {
784 self.plan_key_range
785 }
786
787 #[must_use]
788 pub const fn plan_index_prefix(&self) -> u64 {
789 self.plan_index_prefix
790 }
791
792 #[must_use]
793 pub const fn plan_index_multi_lookup(&self) -> u64 {
794 self.plan_index_multi_lookup
795 }
796
797 #[must_use]
798 pub const fn plan_index_range(&self) -> u64 {
799 self.plan_index_range
800 }
801
802 #[must_use]
803 pub const fn plan_explicit_full_scan(&self) -> u64 {
804 self.plan_explicit_full_scan
805 }
806
807 #[must_use]
808 pub const fn plan_union(&self) -> u64 {
809 self.plan_union
810 }
811
812 #[must_use]
813 pub const fn plan_intersection(&self) -> u64 {
814 self.plan_intersection
815 }
816
817 #[must_use]
818 pub const fn plan_grouped_hash_materialized(&self) -> u64 {
819 self.plan_grouped_hash_materialized
820 }
821
822 #[must_use]
823 pub const fn plan_grouped_ordered_materialized(&self) -> u64 {
824 self.plan_grouped_ordered_materialized
825 }
826
827 #[must_use]
828 pub const fn rows_loaded(&self) -> u64 {
829 self.rows_loaded
830 }
831
832 #[must_use]
833 pub const fn rows_saved(&self) -> u64 {
834 self.rows_saved
835 }
836
837 #[must_use]
838 pub const fn rows_inserted(&self) -> u64 {
839 self.rows_inserted
840 }
841
842 #[must_use]
843 pub const fn rows_updated(&self) -> u64 {
844 self.rows_updated
845 }
846
847 #[must_use]
848 pub const fn rows_replaced(&self) -> u64 {
849 self.rows_replaced
850 }
851
852 #[must_use]
853 pub const fn rows_scanned(&self) -> u64 {
854 self.rows_scanned
855 }
856
857 #[must_use]
858 pub const fn rows_filtered(&self) -> u64 {
859 self.rows_filtered
860 }
861
862 #[must_use]
863 pub const fn rows_aggregated(&self) -> u64 {
864 self.rows_aggregated
865 }
866
867 #[must_use]
868 pub const fn rows_emitted(&self) -> u64 {
869 self.rows_emitted
870 }
871
872 #[must_use]
873 pub const fn rows_deleted(&self) -> u64 {
874 self.rows_deleted
875 }
876
877 #[must_use]
878 pub const fn index_inserts(&self) -> u64 {
879 self.index_inserts
880 }
881
882 #[must_use]
883 pub const fn index_removes(&self) -> u64 {
884 self.index_removes
885 }
886
887 #[must_use]
888 pub const fn reverse_index_inserts(&self) -> u64 {
889 self.reverse_index_inserts
890 }
891
892 #[must_use]
893 pub const fn reverse_index_removes(&self) -> u64 {
894 self.reverse_index_removes
895 }
896
897 #[must_use]
898 pub const fn relation_reverse_lookups(&self) -> u64 {
899 self.relation_reverse_lookups
900 }
901
902 #[must_use]
903 pub const fn relation_delete_blocks(&self) -> u64 {
904 self.relation_delete_blocks
905 }
906
907 #[must_use]
908 pub const fn unique_violations(&self) -> u64 {
909 self.unique_violations
910 }
911
912 #[must_use]
913 pub const fn non_atomic_partial_commits(&self) -> u64 {
914 self.non_atomic_partial_commits
915 }
916
917 #[must_use]
918 pub const fn non_atomic_partial_rows_committed(&self) -> u64 {
919 self.non_atomic_partial_rows_committed
920 }
921
922 const fn activity_score(&self) -> u64 {
925 self.load_calls
926 .saturating_add(self.save_calls)
927 .saturating_add(self.delete_calls)
928 .saturating_add(self.save_insert_calls)
929 .saturating_add(self.save_update_calls)
930 .saturating_add(self.save_replace_calls)
931 .saturating_add(self.exec_success)
932 .saturating_add(self.exec_error_corruption)
933 .saturating_add(self.exec_error_incompatible_persisted_format)
934 .saturating_add(self.exec_error_not_found)
935 .saturating_add(self.exec_error_internal)
936 .saturating_add(self.exec_error_conflict)
937 .saturating_add(self.exec_error_unsupported)
938 .saturating_add(self.exec_error_invariant_violation)
939 .saturating_add(self.exec_aborted)
940 .saturating_add(self.plan_index)
941 .saturating_add(self.plan_keys)
942 .saturating_add(self.plan_range)
943 .saturating_add(self.plan_full_scan)
944 .saturating_add(self.plan_by_key)
945 .saturating_add(self.plan_by_keys)
946 .saturating_add(self.plan_key_range)
947 .saturating_add(self.plan_index_prefix)
948 .saturating_add(self.plan_index_multi_lookup)
949 .saturating_add(self.plan_index_range)
950 .saturating_add(self.plan_explicit_full_scan)
951 .saturating_add(self.plan_union)
952 .saturating_add(self.plan_intersection)
953 .saturating_add(self.plan_grouped_hash_materialized)
954 .saturating_add(self.plan_grouped_ordered_materialized)
955 .saturating_add(self.rows_loaded)
956 .saturating_add(self.rows_saved)
957 .saturating_add(self.rows_inserted)
958 .saturating_add(self.rows_updated)
959 .saturating_add(self.rows_replaced)
960 .saturating_add(self.rows_scanned)
961 .saturating_add(self.rows_filtered)
962 .saturating_add(self.rows_aggregated)
963 .saturating_add(self.rows_emitted)
964 .saturating_add(self.rows_deleted)
965 .saturating_add(self.index_inserts)
966 .saturating_add(self.index_removes)
967 .saturating_add(self.reverse_index_inserts)
968 .saturating_add(self.reverse_index_removes)
969 .saturating_add(self.relation_reverse_lookups)
970 .saturating_add(self.relation_delete_blocks)
971 .saturating_add(self.unique_violations)
972 .saturating_add(self.non_atomic_partial_commits)
973 .saturating_add(self.non_atomic_partial_rows_committed)
974 }
975}
976
977#[must_use]
987pub(super) fn report_window_start(window_start_ms: Option<u64>) -> EventReport {
988 let snap = with_state(Clone::clone);
989 if let Some(requested_window_start_ms) = window_start_ms
990 && requested_window_start_ms > snap.window_start_ms
991 {
992 return EventReport::new(
993 None,
994 Vec::new(),
995 false,
996 window_start_ms,
997 snap.window_start_ms,
998 );
999 }
1000
1001 let mut entity_counters: Vec<EntitySummary> = Vec::new();
1002 for (path, ops) in &snap.entities {
1003 entity_counters.push(EntitySummary {
1004 path: path.clone(),
1005 load_calls: ops.load_calls,
1006 save_calls: ops.save_calls,
1007 delete_calls: ops.delete_calls,
1008 save_insert_calls: ops.save_insert_calls,
1009 save_update_calls: ops.save_update_calls,
1010 save_replace_calls: ops.save_replace_calls,
1011 exec_success: ops.exec_success,
1012 exec_error_corruption: ops.exec_error_corruption,
1013 exec_error_incompatible_persisted_format: ops.exec_error_incompatible_persisted_format,
1014 exec_error_not_found: ops.exec_error_not_found,
1015 exec_error_internal: ops.exec_error_internal,
1016 exec_error_conflict: ops.exec_error_conflict,
1017 exec_error_unsupported: ops.exec_error_unsupported,
1018 exec_error_invariant_violation: ops.exec_error_invariant_violation,
1019 exec_aborted: ops.exec_aborted,
1020 plan_index: ops.plan_index,
1021 plan_keys: ops.plan_keys,
1022 plan_range: ops.plan_range,
1023 plan_full_scan: ops.plan_full_scan,
1024 plan_by_key: ops.plan_by_key,
1025 plan_by_keys: ops.plan_by_keys,
1026 plan_key_range: ops.plan_key_range,
1027 plan_index_prefix: ops.plan_index_prefix,
1028 plan_index_multi_lookup: ops.plan_index_multi_lookup,
1029 plan_index_range: ops.plan_index_range,
1030 plan_explicit_full_scan: ops.plan_explicit_full_scan,
1031 plan_union: ops.plan_union,
1032 plan_intersection: ops.plan_intersection,
1033 plan_grouped_hash_materialized: ops.plan_grouped_hash_materialized,
1034 plan_grouped_ordered_materialized: ops.plan_grouped_ordered_materialized,
1035 rows_loaded: ops.rows_loaded,
1036 rows_saved: ops.rows_saved,
1037 rows_inserted: ops.rows_inserted,
1038 rows_updated: ops.rows_updated,
1039 rows_replaced: ops.rows_replaced,
1040 rows_scanned: ops.rows_scanned,
1041 rows_filtered: ops.rows_filtered,
1042 rows_aggregated: ops.rows_aggregated,
1043 rows_emitted: ops.rows_emitted,
1044 rows_deleted: ops.rows_deleted,
1045 index_inserts: ops.index_inserts,
1046 index_removes: ops.index_removes,
1047 reverse_index_inserts: ops.reverse_index_inserts,
1048 reverse_index_removes: ops.reverse_index_removes,
1049 relation_reverse_lookups: ops.relation_reverse_lookups,
1050 relation_delete_blocks: ops.relation_delete_blocks,
1051 unique_violations: ops.unique_violations,
1052 non_atomic_partial_commits: ops.non_atomic_partial_commits,
1053 non_atomic_partial_rows_committed: ops.non_atomic_partial_rows_committed,
1054 });
1055 }
1056
1057 entity_counters.sort_by(|a, b| {
1058 b.activity_score()
1059 .cmp(&a.activity_score())
1060 .then_with(|| b.rows_loaded.cmp(&a.rows_loaded))
1061 .then_with(|| b.rows_saved.cmp(&a.rows_saved))
1062 .then_with(|| b.rows_scanned.cmp(&a.rows_scanned))
1063 .then_with(|| b.rows_deleted.cmp(&a.rows_deleted))
1064 .then_with(|| a.path.cmp(&b.path))
1065 });
1066
1067 EventReport::new(
1068 Some(EventCounters::new(
1069 snap.ops.clone(),
1070 snap.perf.clone(),
1071 snap.window_start_ms,
1072 now_millis(),
1073 )),
1074 entity_counters,
1075 true,
1076 window_start_ms,
1077 snap.window_start_ms,
1078 )
1079}