Skip to main content

icydb_core/metrics/
state.rs

1//! Module: metrics::state
2//! Responsibility: mutable runtime metrics state and outward report DTOs.
3//! Does not own: instrumentation call sites or sink routing.
4//! Boundary: in-memory metrics state behind the crate-level sink/report surface.
5//!
6//! Runtime metrics are update-only by contract.
7//! Query-side instrumentation is intentionally not surfaced by `report`, so
8//! query metrics are non-existent by design under IC query semantics.
9
10use 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///
35/// MetricRatio
36///
37/// MetricRatio carries a derived metric as an exact raw numerator and
38/// denominator pair. Callers can choose their own decimal rendering policy
39/// without losing precision inside the canister metrics layer.
40///
41#[derive(Clone, Copy, Debug, Eq, PartialEq)]
42pub struct MetricRatio {
43    numerator: u64,
44    denominator: u64,
45}
46
47impl MetricRatio {
48    /// Returns the ratio numerator.
49    #[must_use]
50    pub const fn numerator(&self) -> u64 {
51        self.numerator
52    }
53
54    /// Returns the ratio denominator.
55    #[must_use]
56    pub const fn denominator(&self) -> u64 {
57        self.denominator
58    }
59
60    /// Returns the raw ratio pair.
61    #[must_use]
62    pub const fn into_parts(self) -> (u64, u64) {
63        (self.numerator, self.denominator)
64    }
65}
66
67// Convert raw counter pairs into optional ratio values without encoding a
68// sentinel for "no activity". Consumers can distinguish absent denominators
69// from legitimate zero-valued work.
70const 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    // Executor entrypoints
85    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_shared_query_plan_miss_cold: u64,
105    pub(crate) cache_shared_query_plan_miss_distinct_key: u64,
106    pub(crate) cache_shared_query_plan_miss_method_version: u64,
107    pub(crate) cache_shared_query_plan_miss_schema_fingerprint: u64,
108    pub(crate) cache_shared_query_plan_miss_visibility: u64,
109    pub(crate) cache_sql_compiled_command_hits: u64,
110    pub(crate) cache_sql_compiled_command_misses: u64,
111    pub(crate) cache_sql_compiled_command_inserts: u64,
112    pub(crate) cache_sql_compiled_command_entries: u64,
113    pub(crate) cache_sql_compiled_command_miss_cold: u64,
114    pub(crate) cache_sql_compiled_command_miss_distinct_key: u64,
115    pub(crate) cache_sql_compiled_command_miss_method_version: u64,
116    pub(crate) cache_sql_compiled_command_miss_schema_fingerprint: u64,
117    pub(crate) cache_sql_compiled_command_miss_surface: u64,
118    pub(crate) schema_reconcile_checks: u64,
119    pub(crate) schema_reconcile_exact_match: u64,
120    pub(crate) schema_reconcile_first_create: u64,
121    pub(crate) schema_reconcile_latest_snapshot_corrupt: u64,
122    pub(crate) schema_reconcile_rejected_field_slot: u64,
123    pub(crate) schema_reconcile_rejected_other: u64,
124    pub(crate) schema_reconcile_rejected_row_layout: u64,
125    pub(crate) schema_reconcile_rejected_schema_version: u64,
126    pub(crate) schema_reconcile_store_write_error: u64,
127    pub(crate) schema_transition_checks: u64,
128    pub(crate) schema_transition_append_only_nullable_fields: u64,
129    pub(crate) schema_transition_exact_match: u64,
130    pub(crate) schema_transition_rejected_entity_identity: u64,
131    pub(crate) schema_transition_rejected_field_contract: u64,
132    pub(crate) schema_transition_rejected_field_slot: u64,
133    pub(crate) schema_transition_rejected_row_layout: u64,
134    pub(crate) schema_transition_rejected_schema_version: u64,
135    pub(crate) schema_transition_rejected_snapshot: u64,
136    pub(crate) schema_store_snapshots: u64,
137    pub(crate) schema_store_encoded_bytes: u64,
138    pub(crate) schema_store_latest_snapshot_bytes: u64,
139    pub(crate) accepted_schema_fields: u64,
140    pub(crate) accepted_schema_nested_leaf_facts: u64,
141    pub(crate) sql_compile_rejects: u64,
142    pub(crate) sql_compile_reject_cache_key: u64,
143    pub(crate) sql_compile_reject_parse: u64,
144    pub(crate) sql_compile_reject_semantic: u64,
145
146    // Planner kinds
147    pub(crate) plan_index: u64,
148    pub(crate) plan_keys: u64,
149    pub(crate) plan_range: u64,
150    pub(crate) plan_full_scan: u64,
151    pub(crate) plan_by_key: u64,
152    pub(crate) plan_by_keys: u64,
153    pub(crate) plan_key_range: u64,
154    pub(crate) plan_index_prefix: u64,
155    pub(crate) plan_index_multi_lookup: u64,
156    pub(crate) plan_index_range: u64,
157    pub(crate) plan_explicit_full_scan: u64,
158    pub(crate) plan_union: u64,
159    pub(crate) plan_intersection: u64,
160    pub(crate) plan_grouped_hash_materialized: u64,
161    pub(crate) plan_grouped_ordered_materialized: u64,
162    pub(crate) plan_choice_conflicting_primary_key_children_access_preferred: u64,
163    pub(crate) plan_choice_constant_false_predicate: u64,
164    pub(crate) plan_choice_empty_child_access_preferred: u64,
165    pub(crate) plan_choice_full_scan_access: u64,
166    pub(crate) plan_choice_intent_key_access_override: u64,
167    pub(crate) plan_choice_limit_zero_window: u64,
168    pub(crate) plan_choice_non_index_access: u64,
169    pub(crate) plan_choice_planner_composite_non_index: u64,
170    pub(crate) plan_choice_planner_full_scan_fallback: u64,
171    pub(crate) plan_choice_planner_key_set_access: u64,
172    pub(crate) plan_choice_planner_primary_key_lookup: u64,
173    pub(crate) plan_choice_planner_primary_key_range: u64,
174    pub(crate) plan_choice_required_order_primary_key_range_preferred: u64,
175    pub(crate) plan_choice_singleton_primary_key_child_access_preferred: u64,
176    pub(crate) prepared_shape_already_finalized: u64,
177    pub(crate) prepared_shape_generated_fallback: u64,
178
179    // Rows touched
180    pub(crate) rows_loaded: u64,
181    pub(crate) rows_saved: u64,
182    pub(crate) rows_inserted: u64,
183    pub(crate) rows_updated: u64,
184    pub(crate) rows_replaced: u64,
185    pub(crate) rows_scanned: u64,
186    pub(crate) rows_filtered: u64,
187    pub(crate) rows_aggregated: u64,
188    pub(crate) rows_emitted: u64,
189    pub(crate) load_candidate_rows_scanned: u64,
190    pub(crate) load_candidate_rows_filtered: u64,
191    pub(crate) load_result_rows_emitted: u64,
192    pub(crate) rows_deleted: u64,
193    pub(crate) sql_insert_calls: u64,
194    pub(crate) sql_insert_select_calls: u64,
195    pub(crate) sql_update_calls: u64,
196    pub(crate) sql_delete_calls: u64,
197    pub(crate) sql_write_matched_rows: u64,
198    pub(crate) sql_write_mutated_rows: u64,
199    pub(crate) sql_write_returning_rows: u64,
200    pub(crate) sql_write_error_insert: u64,
201    pub(crate) sql_write_error_insert_select: u64,
202    pub(crate) sql_write_error_update: u64,
203    pub(crate) sql_write_error_delete: u64,
204    pub(crate) sql_write_error_corruption: u64,
205    pub(crate) sql_write_error_incompatible_persisted_format: u64,
206    pub(crate) sql_write_error_not_found: u64,
207    pub(crate) sql_write_error_internal: u64,
208    pub(crate) sql_write_error_conflict: u64,
209    pub(crate) sql_write_error_unsupported: u64,
210    pub(crate) sql_write_error_invariant_violation: u64,
211
212    // Index maintenance
213    pub(crate) index_inserts: u64,
214    pub(crate) index_removes: u64,
215    pub(crate) reverse_index_inserts: u64,
216    pub(crate) reverse_index_removes: u64,
217    pub(crate) relation_reverse_lookups: u64,
218    pub(crate) relation_delete_blocks: u64,
219    pub(crate) write_rows_touched: u64,
220    pub(crate) write_index_entries_changed: u64,
221    pub(crate) write_reverse_index_entries_changed: u64,
222    pub(crate) write_relation_checks: u64,
223    pub(crate) unique_violations: u64,
224    pub(crate) non_atomic_partial_commits: u64,
225    pub(crate) non_atomic_partial_rows_committed: u64,
226}
227
228impl EventOps {
229    #[must_use]
230    pub const fn load_calls(&self) -> u64 {
231        self.load_calls
232    }
233
234    #[must_use]
235    pub const fn save_calls(&self) -> u64 {
236        self.save_calls
237    }
238
239    #[must_use]
240    pub const fn delete_calls(&self) -> u64 {
241        self.delete_calls
242    }
243
244    #[must_use]
245    pub const fn save_insert_calls(&self) -> u64 {
246        self.save_insert_calls
247    }
248
249    #[must_use]
250    pub const fn save_update_calls(&self) -> u64 {
251        self.save_update_calls
252    }
253
254    #[must_use]
255    pub const fn save_replace_calls(&self) -> u64 {
256        self.save_replace_calls
257    }
258
259    #[must_use]
260    pub const fn exec_success(&self) -> u64 {
261        self.exec_success
262    }
263
264    #[must_use]
265    pub const fn exec_error_corruption(&self) -> u64 {
266        self.exec_error_corruption
267    }
268
269    #[must_use]
270    pub const fn exec_error_incompatible_persisted_format(&self) -> u64 {
271        self.exec_error_incompatible_persisted_format
272    }
273
274    #[must_use]
275    pub const fn exec_error_not_found(&self) -> u64 {
276        self.exec_error_not_found
277    }
278
279    #[must_use]
280    pub const fn exec_error_internal(&self) -> u64 {
281        self.exec_error_internal
282    }
283
284    #[must_use]
285    pub const fn exec_error_conflict(&self) -> u64 {
286        self.exec_error_conflict
287    }
288
289    #[must_use]
290    pub const fn exec_error_unsupported(&self) -> u64 {
291        self.exec_error_unsupported
292    }
293
294    #[must_use]
295    pub const fn exec_error_invariant_violation(&self) -> u64 {
296        self.exec_error_invariant_violation
297    }
298
299    #[must_use]
300    pub const fn exec_aborted(&self) -> u64 {
301        self.exec_aborted
302    }
303
304    #[must_use]
305    pub const fn cache_shared_query_plan_hits(&self) -> u64 {
306        self.cache_shared_query_plan_hits
307    }
308
309    #[must_use]
310    pub const fn cache_shared_query_plan_misses(&self) -> u64 {
311        self.cache_shared_query_plan_misses
312    }
313
314    #[must_use]
315    pub const fn cache_shared_query_plan_inserts(&self) -> u64 {
316        self.cache_shared_query_plan_inserts
317    }
318
319    #[must_use]
320    pub const fn cache_shared_query_plan_entries(&self) -> u64 {
321        self.cache_shared_query_plan_entries
322    }
323
324    #[must_use]
325    pub const fn cache_shared_query_plan_miss_cold(&self) -> u64 {
326        self.cache_shared_query_plan_miss_cold
327    }
328
329    #[must_use]
330    pub const fn cache_shared_query_plan_miss_distinct_key(&self) -> u64 {
331        self.cache_shared_query_plan_miss_distinct_key
332    }
333
334    #[must_use]
335    pub const fn cache_shared_query_plan_miss_method_version(&self) -> u64 {
336        self.cache_shared_query_plan_miss_method_version
337    }
338
339    #[must_use]
340    pub const fn cache_shared_query_plan_miss_schema_fingerprint(&self) -> u64 {
341        self.cache_shared_query_plan_miss_schema_fingerprint
342    }
343
344    #[must_use]
345    pub const fn cache_shared_query_plan_miss_visibility(&self) -> u64 {
346        self.cache_shared_query_plan_miss_visibility
347    }
348
349    #[must_use]
350    pub const fn cache_sql_compiled_command_hits(&self) -> u64 {
351        self.cache_sql_compiled_command_hits
352    }
353
354    #[must_use]
355    pub const fn cache_sql_compiled_command_misses(&self) -> u64 {
356        self.cache_sql_compiled_command_misses
357    }
358
359    #[must_use]
360    pub const fn cache_sql_compiled_command_inserts(&self) -> u64 {
361        self.cache_sql_compiled_command_inserts
362    }
363
364    #[must_use]
365    pub const fn cache_sql_compiled_command_entries(&self) -> u64 {
366        self.cache_sql_compiled_command_entries
367    }
368
369    #[must_use]
370    pub const fn cache_sql_compiled_command_miss_cold(&self) -> u64 {
371        self.cache_sql_compiled_command_miss_cold
372    }
373
374    #[must_use]
375    pub const fn cache_sql_compiled_command_miss_distinct_key(&self) -> u64 {
376        self.cache_sql_compiled_command_miss_distinct_key
377    }
378
379    #[must_use]
380    pub const fn cache_sql_compiled_command_miss_method_version(&self) -> u64 {
381        self.cache_sql_compiled_command_miss_method_version
382    }
383
384    #[must_use]
385    pub const fn cache_sql_compiled_command_miss_schema_fingerprint(&self) -> u64 {
386        self.cache_sql_compiled_command_miss_schema_fingerprint
387    }
388
389    #[must_use]
390    pub const fn cache_sql_compiled_command_miss_surface(&self) -> u64 {
391        self.cache_sql_compiled_command_miss_surface
392    }
393
394    #[must_use]
395    pub const fn schema_reconcile_checks(&self) -> u64 {
396        self.schema_reconcile_checks
397    }
398
399    #[must_use]
400    pub const fn schema_reconcile_exact_match(&self) -> u64 {
401        self.schema_reconcile_exact_match
402    }
403
404    #[must_use]
405    pub const fn schema_reconcile_first_create(&self) -> u64 {
406        self.schema_reconcile_first_create
407    }
408
409    #[must_use]
410    pub const fn schema_reconcile_latest_snapshot_corrupt(&self) -> u64 {
411        self.schema_reconcile_latest_snapshot_corrupt
412    }
413
414    #[must_use]
415    pub const fn schema_reconcile_rejected_field_slot(&self) -> u64 {
416        self.schema_reconcile_rejected_field_slot
417    }
418
419    #[must_use]
420    pub const fn schema_reconcile_rejected_other(&self) -> u64 {
421        self.schema_reconcile_rejected_other
422    }
423
424    #[must_use]
425    pub const fn schema_reconcile_rejected_row_layout(&self) -> u64 {
426        self.schema_reconcile_rejected_row_layout
427    }
428
429    #[must_use]
430    pub const fn schema_reconcile_rejected_schema_version(&self) -> u64 {
431        self.schema_reconcile_rejected_schema_version
432    }
433
434    #[must_use]
435    pub const fn schema_reconcile_store_write_error(&self) -> u64 {
436        self.schema_reconcile_store_write_error
437    }
438
439    #[must_use]
440    pub const fn schema_transition_checks(&self) -> u64 {
441        self.schema_transition_checks
442    }
443
444    #[must_use]
445    pub const fn schema_transition_append_only_nullable_fields(&self) -> u64 {
446        self.schema_transition_append_only_nullable_fields
447    }
448
449    #[must_use]
450    pub const fn schema_transition_exact_match(&self) -> u64 {
451        self.schema_transition_exact_match
452    }
453
454    #[must_use]
455    pub const fn schema_transition_rejected_entity_identity(&self) -> u64 {
456        self.schema_transition_rejected_entity_identity
457    }
458
459    #[must_use]
460    pub const fn schema_transition_rejected_field_contract(&self) -> u64 {
461        self.schema_transition_rejected_field_contract
462    }
463
464    #[must_use]
465    pub const fn schema_transition_rejected_field_slot(&self) -> u64 {
466        self.schema_transition_rejected_field_slot
467    }
468
469    #[must_use]
470    pub const fn schema_transition_rejected_row_layout(&self) -> u64 {
471        self.schema_transition_rejected_row_layout
472    }
473
474    #[must_use]
475    pub const fn schema_transition_rejected_schema_version(&self) -> u64 {
476        self.schema_transition_rejected_schema_version
477    }
478
479    #[must_use]
480    pub const fn schema_transition_rejected_snapshot(&self) -> u64 {
481        self.schema_transition_rejected_snapshot
482    }
483
484    #[must_use]
485    pub const fn schema_store_snapshots(&self) -> u64 {
486        self.schema_store_snapshots
487    }
488
489    #[must_use]
490    pub const fn schema_store_encoded_bytes(&self) -> u64 {
491        self.schema_store_encoded_bytes
492    }
493
494    #[must_use]
495    pub const fn schema_store_latest_snapshot_bytes(&self) -> u64 {
496        self.schema_store_latest_snapshot_bytes
497    }
498
499    #[must_use]
500    pub const fn accepted_schema_fields(&self) -> u64 {
501        self.accepted_schema_fields
502    }
503
504    #[must_use]
505    pub const fn accepted_schema_nested_leaf_facts(&self) -> u64 {
506        self.accepted_schema_nested_leaf_facts
507    }
508
509    #[must_use]
510    pub const fn sql_compile_rejects(&self) -> u64 {
511        self.sql_compile_rejects
512    }
513
514    #[must_use]
515    pub const fn sql_compile_reject_cache_key(&self) -> u64 {
516        self.sql_compile_reject_cache_key
517    }
518
519    #[must_use]
520    pub const fn sql_compile_reject_parse(&self) -> u64 {
521        self.sql_compile_reject_parse
522    }
523
524    #[must_use]
525    pub const fn sql_compile_reject_semantic(&self) -> u64 {
526        self.sql_compile_reject_semantic
527    }
528
529    #[must_use]
530    pub const fn plan_index(&self) -> u64 {
531        self.plan_index
532    }
533
534    #[must_use]
535    pub const fn plan_keys(&self) -> u64 {
536        self.plan_keys
537    }
538
539    #[must_use]
540    pub const fn plan_range(&self) -> u64 {
541        self.plan_range
542    }
543
544    #[must_use]
545    pub const fn plan_full_scan(&self) -> u64 {
546        self.plan_full_scan
547    }
548
549    #[must_use]
550    pub const fn plan_by_key(&self) -> u64 {
551        self.plan_by_key
552    }
553
554    #[must_use]
555    pub const fn plan_by_keys(&self) -> u64 {
556        self.plan_by_keys
557    }
558
559    #[must_use]
560    pub const fn plan_key_range(&self) -> u64 {
561        self.plan_key_range
562    }
563
564    #[must_use]
565    pub const fn plan_index_prefix(&self) -> u64 {
566        self.plan_index_prefix
567    }
568
569    #[must_use]
570    pub const fn plan_index_multi_lookup(&self) -> u64 {
571        self.plan_index_multi_lookup
572    }
573
574    #[must_use]
575    pub const fn plan_index_range(&self) -> u64 {
576        self.plan_index_range
577    }
578
579    #[must_use]
580    pub const fn plan_explicit_full_scan(&self) -> u64 {
581        self.plan_explicit_full_scan
582    }
583
584    #[must_use]
585    pub const fn plan_union(&self) -> u64 {
586        self.plan_union
587    }
588
589    #[must_use]
590    pub const fn plan_intersection(&self) -> u64 {
591        self.plan_intersection
592    }
593
594    #[must_use]
595    pub const fn plan_grouped_hash_materialized(&self) -> u64 {
596        self.plan_grouped_hash_materialized
597    }
598
599    #[must_use]
600    pub const fn plan_grouped_ordered_materialized(&self) -> u64 {
601        self.plan_grouped_ordered_materialized
602    }
603
604    #[must_use]
605    pub const fn plan_choice_conflicting_primary_key_children_access_preferred(&self) -> u64 {
606        self.plan_choice_conflicting_primary_key_children_access_preferred
607    }
608
609    #[must_use]
610    pub const fn plan_choice_constant_false_predicate(&self) -> u64 {
611        self.plan_choice_constant_false_predicate
612    }
613
614    #[must_use]
615    pub const fn plan_choice_empty_child_access_preferred(&self) -> u64 {
616        self.plan_choice_empty_child_access_preferred
617    }
618
619    #[must_use]
620    pub const fn plan_choice_full_scan_access(&self) -> u64 {
621        self.plan_choice_full_scan_access
622    }
623
624    #[must_use]
625    pub const fn plan_choice_intent_key_access_override(&self) -> u64 {
626        self.plan_choice_intent_key_access_override
627    }
628
629    #[must_use]
630    pub const fn plan_choice_limit_zero_window(&self) -> u64 {
631        self.plan_choice_limit_zero_window
632    }
633
634    #[must_use]
635    pub const fn plan_choice_non_index_access(&self) -> u64 {
636        self.plan_choice_non_index_access
637    }
638
639    #[must_use]
640    pub const fn plan_choice_planner_composite_non_index(&self) -> u64 {
641        self.plan_choice_planner_composite_non_index
642    }
643
644    #[must_use]
645    pub const fn plan_choice_planner_full_scan_fallback(&self) -> u64 {
646        self.plan_choice_planner_full_scan_fallback
647    }
648
649    #[must_use]
650    pub const fn plan_choice_planner_key_set_access(&self) -> u64 {
651        self.plan_choice_planner_key_set_access
652    }
653
654    #[must_use]
655    pub const fn plan_choice_planner_primary_key_lookup(&self) -> u64 {
656        self.plan_choice_planner_primary_key_lookup
657    }
658
659    #[must_use]
660    pub const fn plan_choice_planner_primary_key_range(&self) -> u64 {
661        self.plan_choice_planner_primary_key_range
662    }
663
664    #[must_use]
665    pub const fn plan_choice_required_order_primary_key_range_preferred(&self) -> u64 {
666        self.plan_choice_required_order_primary_key_range_preferred
667    }
668
669    #[must_use]
670    pub const fn plan_choice_singleton_primary_key_child_access_preferred(&self) -> u64 {
671        self.plan_choice_singleton_primary_key_child_access_preferred
672    }
673
674    #[must_use]
675    pub const fn prepared_shape_already_finalized(&self) -> u64 {
676        self.prepared_shape_already_finalized
677    }
678
679    #[must_use]
680    pub const fn prepared_shape_generated_fallback(&self) -> u64 {
681        self.prepared_shape_generated_fallback
682    }
683
684    #[must_use]
685    pub const fn rows_loaded(&self) -> u64 {
686        self.rows_loaded
687    }
688
689    #[must_use]
690    pub const fn rows_saved(&self) -> u64 {
691        self.rows_saved
692    }
693
694    #[must_use]
695    pub const fn rows_inserted(&self) -> u64 {
696        self.rows_inserted
697    }
698
699    #[must_use]
700    pub const fn rows_updated(&self) -> u64 {
701        self.rows_updated
702    }
703
704    #[must_use]
705    pub const fn rows_replaced(&self) -> u64 {
706        self.rows_replaced
707    }
708
709    #[must_use]
710    pub const fn rows_scanned(&self) -> u64 {
711        self.rows_scanned
712    }
713
714    #[must_use]
715    pub const fn rows_filtered(&self) -> u64 {
716        self.rows_filtered
717    }
718
719    #[must_use]
720    pub const fn rows_aggregated(&self) -> u64 {
721        self.rows_aggregated
722    }
723
724    #[must_use]
725    pub const fn rows_emitted(&self) -> u64 {
726        self.rows_emitted
727    }
728
729    #[must_use]
730    pub const fn load_candidate_rows_scanned(&self) -> u64 {
731        self.load_candidate_rows_scanned
732    }
733
734    #[must_use]
735    pub const fn load_candidate_rows_filtered(&self) -> u64 {
736        self.load_candidate_rows_filtered
737    }
738
739    #[must_use]
740    pub const fn load_result_rows_emitted(&self) -> u64 {
741        self.load_result_rows_emitted
742    }
743
744    #[must_use]
745    pub const fn rows_deleted(&self) -> u64 {
746        self.rows_deleted
747    }
748
749    #[must_use]
750    pub const fn sql_insert_calls(&self) -> u64 {
751        self.sql_insert_calls
752    }
753
754    #[must_use]
755    pub const fn sql_insert_select_calls(&self) -> u64 {
756        self.sql_insert_select_calls
757    }
758
759    #[must_use]
760    pub const fn sql_update_calls(&self) -> u64 {
761        self.sql_update_calls
762    }
763
764    #[must_use]
765    pub const fn sql_delete_calls(&self) -> u64 {
766        self.sql_delete_calls
767    }
768
769    #[must_use]
770    pub const fn sql_write_matched_rows(&self) -> u64 {
771        self.sql_write_matched_rows
772    }
773
774    #[must_use]
775    pub const fn sql_write_mutated_rows(&self) -> u64 {
776        self.sql_write_mutated_rows
777    }
778
779    #[must_use]
780    pub const fn sql_write_returning_rows(&self) -> u64 {
781        self.sql_write_returning_rows
782    }
783
784    #[must_use]
785    pub const fn sql_write_error_insert(&self) -> u64 {
786        self.sql_write_error_insert
787    }
788
789    #[must_use]
790    pub const fn sql_write_error_insert_select(&self) -> u64 {
791        self.sql_write_error_insert_select
792    }
793
794    #[must_use]
795    pub const fn sql_write_error_update(&self) -> u64 {
796        self.sql_write_error_update
797    }
798
799    #[must_use]
800    pub const fn sql_write_error_delete(&self) -> u64 {
801        self.sql_write_error_delete
802    }
803
804    #[must_use]
805    pub const fn sql_write_error_corruption(&self) -> u64 {
806        self.sql_write_error_corruption
807    }
808
809    #[must_use]
810    pub const fn sql_write_error_incompatible_persisted_format(&self) -> u64 {
811        self.sql_write_error_incompatible_persisted_format
812    }
813
814    #[must_use]
815    pub const fn sql_write_error_not_found(&self) -> u64 {
816        self.sql_write_error_not_found
817    }
818
819    #[must_use]
820    pub const fn sql_write_error_internal(&self) -> u64 {
821        self.sql_write_error_internal
822    }
823
824    #[must_use]
825    pub const fn sql_write_error_conflict(&self) -> u64 {
826        self.sql_write_error_conflict
827    }
828
829    #[must_use]
830    pub const fn sql_write_error_unsupported(&self) -> u64 {
831        self.sql_write_error_unsupported
832    }
833
834    #[must_use]
835    pub const fn sql_write_error_invariant_violation(&self) -> u64 {
836        self.sql_write_error_invariant_violation
837    }
838
839    #[must_use]
840    pub const fn index_inserts(&self) -> u64 {
841        self.index_inserts
842    }
843
844    #[must_use]
845    pub const fn index_removes(&self) -> u64 {
846        self.index_removes
847    }
848
849    #[must_use]
850    pub const fn reverse_index_inserts(&self) -> u64 {
851        self.reverse_index_inserts
852    }
853
854    #[must_use]
855    pub const fn reverse_index_removes(&self) -> u64 {
856        self.reverse_index_removes
857    }
858
859    #[must_use]
860    pub const fn relation_reverse_lookups(&self) -> u64 {
861        self.relation_reverse_lookups
862    }
863
864    #[must_use]
865    pub const fn relation_delete_blocks(&self) -> u64 {
866        self.relation_delete_blocks
867    }
868
869    #[must_use]
870    pub const fn write_rows_touched(&self) -> u64 {
871        self.write_rows_touched
872    }
873
874    #[must_use]
875    pub const fn write_index_entries_changed(&self) -> u64 {
876        self.write_index_entries_changed
877    }
878
879    #[must_use]
880    pub const fn write_reverse_index_entries_changed(&self) -> u64 {
881        self.write_reverse_index_entries_changed
882    }
883
884    #[must_use]
885    pub const fn write_relation_checks(&self) -> u64 {
886        self.write_relation_checks
887    }
888
889    #[must_use]
890    pub const fn unique_violations(&self) -> u64 {
891        self.unique_violations
892    }
893
894    #[must_use]
895    pub const fn non_atomic_partial_commits(&self) -> u64 {
896        self.non_atomic_partial_commits
897    }
898
899    #[must_use]
900    pub const fn non_atomic_partial_rows_committed(&self) -> u64 {
901        self.non_atomic_partial_rows_committed
902    }
903
904    /// Returns result rows emitted per load candidate row scanned.
905    #[must_use]
906    pub const fn load_selectivity_ratio(&self) -> Option<MetricRatio> {
907        ratio(
908            self.load_result_rows_emitted,
909            self.load_candidate_rows_scanned,
910        )
911    }
912
913    /// Returns candidate rows filtered per load candidate row scanned.
914    #[must_use]
915    pub const fn load_filter_ratio(&self) -> Option<MetricRatio> {
916        ratio(
917            self.load_candidate_rows_filtered,
918            self.load_candidate_rows_scanned,
919        )
920    }
921
922    /// Returns SQL-mutated rows per SQL-matched row.
923    #[must_use]
924    pub const fn sql_write_mutation_ratio(&self) -> Option<MetricRatio> {
925        ratio(self.sql_write_mutated_rows, self.sql_write_matched_rows)
926    }
927
928    /// Returns SQL `RETURNING` rows per SQL-mutated row.
929    #[must_use]
930    pub const fn sql_write_returning_ratio(&self) -> Option<MetricRatio> {
931        ratio(self.sql_write_returning_rows, self.sql_write_mutated_rows)
932    }
933
934    /// Returns primary index entries changed per write row touched.
935    #[must_use]
936    pub const fn write_index_entries_per_row(&self) -> Option<MetricRatio> {
937        ratio(self.write_index_entries_changed, self.write_rows_touched)
938    }
939
940    /// Returns reverse-index entries changed per write row touched.
941    #[must_use]
942    pub const fn write_reverse_index_entries_per_row(&self) -> Option<MetricRatio> {
943        ratio(
944            self.write_reverse_index_entries_changed,
945            self.write_rows_touched,
946        )
947    }
948
949    /// Returns relation checks performed per write row touched.
950    #[must_use]
951    pub const fn write_relation_checks_per_row(&self) -> Option<MetricRatio> {
952        ratio(self.write_relation_checks, self.write_rows_touched)
953    }
954}
955
956#[derive(Clone, Debug, Default)]
957pub(crate) struct EntityCounters {
958    pub(crate) load_calls: u64,
959    pub(crate) save_calls: u64,
960    pub(crate) delete_calls: u64,
961    pub(crate) save_insert_calls: u64,
962    pub(crate) save_update_calls: u64,
963    pub(crate) save_replace_calls: u64,
964    pub(crate) exec_success: u64,
965    pub(crate) exec_error_corruption: u64,
966    pub(crate) exec_error_incompatible_persisted_format: u64,
967    pub(crate) exec_error_not_found: u64,
968    pub(crate) exec_error_internal: u64,
969    pub(crate) exec_error_conflict: u64,
970    pub(crate) exec_error_unsupported: u64,
971    pub(crate) exec_error_invariant_violation: u64,
972    pub(crate) exec_aborted: u64,
973    pub(crate) cache_shared_query_plan_hits: u64,
974    pub(crate) cache_shared_query_plan_misses: u64,
975    pub(crate) cache_shared_query_plan_inserts: u64,
976    pub(crate) cache_shared_query_plan_miss_cold: u64,
977    pub(crate) cache_shared_query_plan_miss_distinct_key: u64,
978    pub(crate) cache_shared_query_plan_miss_method_version: u64,
979    pub(crate) cache_shared_query_plan_miss_schema_fingerprint: u64,
980    pub(crate) cache_shared_query_plan_miss_visibility: u64,
981    pub(crate) cache_sql_compiled_command_hits: u64,
982    pub(crate) cache_sql_compiled_command_misses: u64,
983    pub(crate) cache_sql_compiled_command_inserts: u64,
984    pub(crate) cache_sql_compiled_command_miss_cold: u64,
985    pub(crate) cache_sql_compiled_command_miss_distinct_key: u64,
986    pub(crate) cache_sql_compiled_command_miss_method_version: u64,
987    pub(crate) cache_sql_compiled_command_miss_schema_fingerprint: u64,
988    pub(crate) cache_sql_compiled_command_miss_surface: u64,
989    pub(crate) schema_reconcile_checks: u64,
990    pub(crate) schema_reconcile_exact_match: u64,
991    pub(crate) schema_reconcile_first_create: u64,
992    pub(crate) schema_reconcile_latest_snapshot_corrupt: u64,
993    pub(crate) schema_reconcile_rejected_field_slot: u64,
994    pub(crate) schema_reconcile_rejected_other: u64,
995    pub(crate) schema_reconcile_rejected_row_layout: u64,
996    pub(crate) schema_reconcile_rejected_schema_version: u64,
997    pub(crate) schema_reconcile_store_write_error: u64,
998    pub(crate) schema_transition_checks: u64,
999    pub(crate) schema_transition_append_only_nullable_fields: u64,
1000    pub(crate) schema_transition_exact_match: u64,
1001    pub(crate) schema_transition_rejected_entity_identity: u64,
1002    pub(crate) schema_transition_rejected_field_contract: u64,
1003    pub(crate) schema_transition_rejected_field_slot: u64,
1004    pub(crate) schema_transition_rejected_row_layout: u64,
1005    pub(crate) schema_transition_rejected_schema_version: u64,
1006    pub(crate) schema_transition_rejected_snapshot: u64,
1007    pub(crate) schema_store_snapshots: u64,
1008    pub(crate) schema_store_encoded_bytes: u64,
1009    pub(crate) schema_store_latest_snapshot_bytes: u64,
1010    pub(crate) accepted_schema_fields: u64,
1011    pub(crate) accepted_schema_nested_leaf_facts: u64,
1012    pub(crate) sql_compile_rejects: u64,
1013    pub(crate) sql_compile_reject_cache_key: u64,
1014    pub(crate) sql_compile_reject_parse: u64,
1015    pub(crate) sql_compile_reject_semantic: u64,
1016    pub(crate) plan_index: u64,
1017    pub(crate) plan_keys: u64,
1018    pub(crate) plan_range: u64,
1019    pub(crate) plan_full_scan: u64,
1020    pub(crate) plan_by_key: u64,
1021    pub(crate) plan_by_keys: u64,
1022    pub(crate) plan_key_range: u64,
1023    pub(crate) plan_index_prefix: u64,
1024    pub(crate) plan_index_multi_lookup: u64,
1025    pub(crate) plan_index_range: u64,
1026    pub(crate) plan_explicit_full_scan: u64,
1027    pub(crate) plan_union: u64,
1028    pub(crate) plan_intersection: u64,
1029    pub(crate) plan_grouped_hash_materialized: u64,
1030    pub(crate) plan_grouped_ordered_materialized: u64,
1031    pub(crate) plan_choice_conflicting_primary_key_children_access_preferred: u64,
1032    pub(crate) plan_choice_constant_false_predicate: u64,
1033    pub(crate) plan_choice_empty_child_access_preferred: u64,
1034    pub(crate) plan_choice_full_scan_access: u64,
1035    pub(crate) plan_choice_intent_key_access_override: u64,
1036    pub(crate) plan_choice_limit_zero_window: u64,
1037    pub(crate) plan_choice_non_index_access: u64,
1038    pub(crate) plan_choice_planner_composite_non_index: u64,
1039    pub(crate) plan_choice_planner_full_scan_fallback: u64,
1040    pub(crate) plan_choice_planner_key_set_access: u64,
1041    pub(crate) plan_choice_planner_primary_key_lookup: u64,
1042    pub(crate) plan_choice_planner_primary_key_range: u64,
1043    pub(crate) plan_choice_required_order_primary_key_range_preferred: u64,
1044    pub(crate) plan_choice_singleton_primary_key_child_access_preferred: u64,
1045    pub(crate) prepared_shape_already_finalized: u64,
1046    pub(crate) prepared_shape_generated_fallback: u64,
1047    pub(crate) rows_loaded: u64,
1048    pub(crate) rows_saved: u64,
1049    pub(crate) rows_inserted: u64,
1050    pub(crate) rows_updated: u64,
1051    pub(crate) rows_replaced: u64,
1052    pub(crate) rows_scanned: u64,
1053    pub(crate) rows_filtered: u64,
1054    pub(crate) rows_aggregated: u64,
1055    pub(crate) rows_emitted: u64,
1056    pub(crate) load_candidate_rows_scanned: u64,
1057    pub(crate) load_candidate_rows_filtered: u64,
1058    pub(crate) load_result_rows_emitted: u64,
1059    pub(crate) rows_deleted: u64,
1060    pub(crate) sql_insert_calls: u64,
1061    pub(crate) sql_insert_select_calls: u64,
1062    pub(crate) sql_update_calls: u64,
1063    pub(crate) sql_delete_calls: u64,
1064    pub(crate) sql_write_matched_rows: u64,
1065    pub(crate) sql_write_mutated_rows: u64,
1066    pub(crate) sql_write_returning_rows: u64,
1067    pub(crate) sql_write_error_insert: u64,
1068    pub(crate) sql_write_error_insert_select: u64,
1069    pub(crate) sql_write_error_update: u64,
1070    pub(crate) sql_write_error_delete: u64,
1071    pub(crate) sql_write_error_corruption: u64,
1072    pub(crate) sql_write_error_incompatible_persisted_format: u64,
1073    pub(crate) sql_write_error_not_found: u64,
1074    pub(crate) sql_write_error_internal: u64,
1075    pub(crate) sql_write_error_conflict: u64,
1076    pub(crate) sql_write_error_unsupported: u64,
1077    pub(crate) sql_write_error_invariant_violation: u64,
1078    pub(crate) index_inserts: u64,
1079    pub(crate) index_removes: u64,
1080    pub(crate) reverse_index_inserts: u64,
1081    pub(crate) reverse_index_removes: u64,
1082    pub(crate) relation_reverse_lookups: u64,
1083    pub(crate) relation_delete_blocks: u64,
1084    pub(crate) write_rows_touched: u64,
1085    pub(crate) write_index_entries_changed: u64,
1086    pub(crate) write_reverse_index_entries_changed: u64,
1087    pub(crate) write_relation_checks: u64,
1088    pub(crate) unique_violations: u64,
1089    pub(crate) non_atomic_partial_commits: u64,
1090    pub(crate) non_atomic_partial_rows_committed: u64,
1091}
1092
1093#[cfg_attr(doc, doc = "EventPerf\n\nInstruction totals and maxima.")]
1094#[derive(CandidType, Clone, Debug, Default, Deserialize)]
1095pub struct EventPerf {
1096    // Instruction totals per executor (ic_cdk::api::performance_counter(1))
1097    pub(crate) load_inst_total: u128,
1098    pub(crate) save_inst_total: u128,
1099    pub(crate) delete_inst_total: u128,
1100
1101    // Maximum observed instruction deltas
1102    pub(crate) load_inst_max: u64,
1103    pub(crate) save_inst_max: u64,
1104    pub(crate) delete_inst_max: u64,
1105}
1106
1107impl EventPerf {
1108    #[must_use]
1109    pub const fn new(
1110        load_inst_total: u128,
1111        save_inst_total: u128,
1112        delete_inst_total: u128,
1113        load_inst_max: u64,
1114        save_inst_max: u64,
1115        delete_inst_max: u64,
1116    ) -> Self {
1117        Self {
1118            load_inst_total,
1119            save_inst_total,
1120            delete_inst_total,
1121            load_inst_max,
1122            save_inst_max,
1123            delete_inst_max,
1124        }
1125    }
1126
1127    #[must_use]
1128    pub const fn load_inst_total(&self) -> u128 {
1129        self.load_inst_total
1130    }
1131
1132    #[must_use]
1133    pub const fn save_inst_total(&self) -> u128 {
1134        self.save_inst_total
1135    }
1136
1137    #[must_use]
1138    pub const fn delete_inst_total(&self) -> u128 {
1139        self.delete_inst_total
1140    }
1141
1142    #[must_use]
1143    pub const fn load_inst_max(&self) -> u64 {
1144        self.load_inst_max
1145    }
1146
1147    #[must_use]
1148    pub const fn save_inst_max(&self) -> u64 {
1149        self.save_inst_max
1150    }
1151
1152    #[must_use]
1153    pub const fn delete_inst_max(&self) -> u64 {
1154        self.delete_inst_max
1155    }
1156}
1157
1158thread_local! {
1159    static EVENT_STATE: RefCell<EventState> = RefCell::new(EventState::default());
1160}
1161
1162// Borrow metrics immutably.
1163pub(crate) fn with_state<R>(f: impl FnOnce(&EventState) -> R) -> R {
1164    EVENT_STATE.with(|m| f(&m.borrow()))
1165}
1166
1167// Borrow metrics mutably.
1168pub(crate) fn with_state_mut<R>(f: impl FnOnce(&mut EventState) -> R) -> R {
1169    EVENT_STATE.with(|m| f(&mut m.borrow_mut()))
1170}
1171
1172// Reset all counters (useful in tests).
1173pub(super) fn reset() {
1174    with_state_mut(|m| *m = EventState::default());
1175}
1176
1177// Reset all event state: counters, perf, and serialize counters.
1178pub(crate) fn reset_all() {
1179    reset();
1180}
1181
1182// Accumulate instruction counts and track a max.
1183pub(super) fn add_instructions(total: &mut u128, max: &mut u64, delta_inst: u64) {
1184    *total = total.saturating_add(u128::from(delta_inst));
1185    if delta_inst > *max {
1186        *max = delta_inst;
1187    }
1188}
1189
1190#[cfg_attr(doc, doc = "EventReport\n\nMetrics query payload.")]
1191#[derive(CandidType, Clone, Debug, Default, Deserialize)]
1192pub struct EventReport {
1193    counters: Option<EventCounters>,
1194    entity_counters: Vec<EntitySummary>,
1195    window_filter_matched: bool,
1196    requested_window_start_ms: Option<u64>,
1197    active_window_start_ms: u64,
1198}
1199
1200impl EventReport {
1201    #[must_use]
1202    pub(crate) const fn new(
1203        counters: Option<EventCounters>,
1204        entity_counters: Vec<EntitySummary>,
1205        window_filter_matched: bool,
1206        requested_window_start_ms: Option<u64>,
1207        active_window_start_ms: u64,
1208    ) -> Self {
1209        Self {
1210            counters,
1211            entity_counters,
1212            window_filter_matched,
1213            requested_window_start_ms,
1214            active_window_start_ms,
1215        }
1216    }
1217
1218    #[must_use]
1219    pub const fn counters(&self) -> Option<&EventCounters> {
1220        self.counters.as_ref()
1221    }
1222
1223    #[must_use]
1224    pub fn entity_counters(&self) -> &[EntitySummary] {
1225        &self.entity_counters
1226    }
1227
1228    #[must_use]
1229    pub const fn window_filter_matched(&self) -> bool {
1230        self.window_filter_matched
1231    }
1232
1233    #[must_use]
1234    pub const fn requested_window_start_ms(&self) -> Option<u64> {
1235        self.requested_window_start_ms
1236    }
1237
1238    #[must_use]
1239    pub const fn active_window_start_ms(&self) -> u64 {
1240        self.active_window_start_ms
1241    }
1242
1243    #[must_use]
1244    pub fn into_counters(self) -> Option<EventCounters> {
1245        self.counters
1246    }
1247
1248    #[must_use]
1249    pub fn into_entity_counters(self) -> Vec<EntitySummary> {
1250        self.entity_counters
1251    }
1252}
1253
1254//
1255// EventCounters
1256//
1257// Top-level metrics counters returned by `icydb_metrics()`.
1258// This keeps aggregate ops/perf totals while leaving per-entity detail to the
1259// separate `entity_counters` payload.
1260//
1261
1262#[derive(CandidType, Clone, Debug, Default, Deserialize)]
1263pub struct EventCounters {
1264    pub(crate) ops: EventOps,
1265    pub(crate) perf: EventPerf,
1266    pub(crate) window_start_ms: u64,
1267    pub(crate) window_end_ms: u64,
1268    pub(crate) window_duration_ms: u64,
1269}
1270
1271impl EventCounters {
1272    #[must_use]
1273    pub(crate) const fn new(
1274        ops: EventOps,
1275        perf: EventPerf,
1276        window_start_ms: u64,
1277        window_end_ms: u64,
1278    ) -> Self {
1279        Self {
1280            ops,
1281            perf,
1282            window_start_ms,
1283            window_end_ms,
1284            window_duration_ms: window_end_ms.saturating_sub(window_start_ms),
1285        }
1286    }
1287
1288    #[must_use]
1289    pub const fn ops(&self) -> &EventOps {
1290        &self.ops
1291    }
1292
1293    #[must_use]
1294    pub const fn perf(&self) -> &EventPerf {
1295        &self.perf
1296    }
1297
1298    #[must_use]
1299    pub const fn window_start_ms(&self) -> u64 {
1300        self.window_start_ms
1301    }
1302
1303    #[must_use]
1304    pub const fn window_end_ms(&self) -> u64 {
1305        self.window_end_ms
1306    }
1307
1308    #[must_use]
1309    pub const fn window_duration_ms(&self) -> u64 {
1310        self.window_duration_ms
1311    }
1312}
1313
1314#[cfg_attr(doc, doc = "EntitySummary\n\nPer-entity metrics summary.")]
1315#[derive(CandidType, Clone, Debug, Default, Deserialize)]
1316pub struct EntitySummary {
1317    path: String,
1318    load_calls: u64,
1319    save_calls: u64,
1320    delete_calls: u64,
1321    save_insert_calls: u64,
1322    save_update_calls: u64,
1323    save_replace_calls: u64,
1324    exec_success: u64,
1325    exec_error_corruption: u64,
1326    exec_error_incompatible_persisted_format: u64,
1327    exec_error_not_found: u64,
1328    exec_error_internal: u64,
1329    exec_error_conflict: u64,
1330    exec_error_unsupported: u64,
1331    exec_error_invariant_violation: u64,
1332    exec_aborted: u64,
1333    cache_shared_query_plan_hits: u64,
1334    cache_shared_query_plan_misses: u64,
1335    cache_shared_query_plan_inserts: u64,
1336    cache_shared_query_plan_miss_cold: u64,
1337    cache_shared_query_plan_miss_distinct_key: u64,
1338    cache_shared_query_plan_miss_method_version: u64,
1339    cache_shared_query_plan_miss_schema_fingerprint: u64,
1340    cache_shared_query_plan_miss_visibility: u64,
1341    cache_sql_compiled_command_hits: u64,
1342    cache_sql_compiled_command_misses: u64,
1343    cache_sql_compiled_command_inserts: u64,
1344    cache_sql_compiled_command_miss_cold: u64,
1345    cache_sql_compiled_command_miss_distinct_key: u64,
1346    cache_sql_compiled_command_miss_method_version: u64,
1347    cache_sql_compiled_command_miss_schema_fingerprint: u64,
1348    cache_sql_compiled_command_miss_surface: u64,
1349    schema_reconcile_checks: u64,
1350    schema_reconcile_exact_match: u64,
1351    schema_reconcile_first_create: u64,
1352    schema_reconcile_latest_snapshot_corrupt: u64,
1353    schema_reconcile_rejected_field_slot: u64,
1354    schema_reconcile_rejected_other: u64,
1355    schema_reconcile_rejected_row_layout: u64,
1356    schema_reconcile_rejected_schema_version: u64,
1357    schema_reconcile_store_write_error: u64,
1358    schema_transition_checks: u64,
1359    schema_transition_append_only_nullable_fields: u64,
1360    schema_transition_exact_match: u64,
1361    schema_transition_rejected_entity_identity: u64,
1362    schema_transition_rejected_field_contract: u64,
1363    schema_transition_rejected_field_slot: u64,
1364    schema_transition_rejected_row_layout: u64,
1365    schema_transition_rejected_schema_version: u64,
1366    schema_transition_rejected_snapshot: u64,
1367    schema_store_snapshots: u64,
1368    schema_store_encoded_bytes: u64,
1369    schema_store_latest_snapshot_bytes: u64,
1370    accepted_schema_fields: u64,
1371    accepted_schema_nested_leaf_facts: u64,
1372    sql_compile_rejects: u64,
1373    sql_compile_reject_cache_key: u64,
1374    sql_compile_reject_parse: u64,
1375    sql_compile_reject_semantic: u64,
1376    plan_index: u64,
1377    plan_keys: u64,
1378    plan_range: u64,
1379    plan_full_scan: u64,
1380    plan_by_key: u64,
1381    plan_by_keys: u64,
1382    plan_key_range: u64,
1383    plan_index_prefix: u64,
1384    plan_index_multi_lookup: u64,
1385    plan_index_range: u64,
1386    plan_explicit_full_scan: u64,
1387    plan_union: u64,
1388    plan_intersection: u64,
1389    plan_grouped_hash_materialized: u64,
1390    plan_grouped_ordered_materialized: u64,
1391    plan_choice_conflicting_primary_key_children_access_preferred: u64,
1392    plan_choice_constant_false_predicate: u64,
1393    plan_choice_empty_child_access_preferred: u64,
1394    plan_choice_full_scan_access: u64,
1395    plan_choice_intent_key_access_override: u64,
1396    plan_choice_limit_zero_window: u64,
1397    plan_choice_non_index_access: u64,
1398    plan_choice_planner_composite_non_index: u64,
1399    plan_choice_planner_full_scan_fallback: u64,
1400    plan_choice_planner_key_set_access: u64,
1401    plan_choice_planner_primary_key_lookup: u64,
1402    plan_choice_planner_primary_key_range: u64,
1403    plan_choice_required_order_primary_key_range_preferred: u64,
1404    plan_choice_singleton_primary_key_child_access_preferred: u64,
1405    prepared_shape_already_finalized: u64,
1406    prepared_shape_generated_fallback: u64,
1407    rows_loaded: u64,
1408    rows_saved: u64,
1409    rows_inserted: u64,
1410    rows_updated: u64,
1411    rows_replaced: u64,
1412    rows_scanned: u64,
1413    rows_filtered: u64,
1414    rows_aggregated: u64,
1415    rows_emitted: u64,
1416    load_candidate_rows_scanned: u64,
1417    load_candidate_rows_filtered: u64,
1418    load_result_rows_emitted: u64,
1419    rows_deleted: u64,
1420    sql_insert_calls: u64,
1421    sql_insert_select_calls: u64,
1422    sql_update_calls: u64,
1423    sql_delete_calls: u64,
1424    sql_write_matched_rows: u64,
1425    sql_write_mutated_rows: u64,
1426    sql_write_returning_rows: u64,
1427    sql_write_error_insert: u64,
1428    sql_write_error_insert_select: u64,
1429    sql_write_error_update: u64,
1430    sql_write_error_delete: u64,
1431    sql_write_error_corruption: u64,
1432    sql_write_error_incompatible_persisted_format: u64,
1433    sql_write_error_not_found: u64,
1434    sql_write_error_internal: u64,
1435    sql_write_error_conflict: u64,
1436    sql_write_error_unsupported: u64,
1437    sql_write_error_invariant_violation: u64,
1438    index_inserts: u64,
1439    index_removes: u64,
1440    reverse_index_inserts: u64,
1441    reverse_index_removes: u64,
1442    relation_reverse_lookups: u64,
1443    relation_delete_blocks: u64,
1444    write_rows_touched: u64,
1445    write_index_entries_changed: u64,
1446    write_reverse_index_entries_changed: u64,
1447    write_relation_checks: u64,
1448    unique_violations: u64,
1449    non_atomic_partial_commits: u64,
1450    non_atomic_partial_rows_committed: u64,
1451}
1452
1453impl EntitySummary {
1454    #[must_use]
1455    pub const fn path(&self) -> &str {
1456        self.path.as_str()
1457    }
1458
1459    #[must_use]
1460    pub const fn load_calls(&self) -> u64 {
1461        self.load_calls
1462    }
1463
1464    #[must_use]
1465    pub const fn save_calls(&self) -> u64 {
1466        self.save_calls
1467    }
1468
1469    #[must_use]
1470    pub const fn delete_calls(&self) -> u64 {
1471        self.delete_calls
1472    }
1473
1474    #[must_use]
1475    pub const fn save_insert_calls(&self) -> u64 {
1476        self.save_insert_calls
1477    }
1478
1479    #[must_use]
1480    pub const fn save_update_calls(&self) -> u64 {
1481        self.save_update_calls
1482    }
1483
1484    #[must_use]
1485    pub const fn save_replace_calls(&self) -> u64 {
1486        self.save_replace_calls
1487    }
1488
1489    #[must_use]
1490    pub const fn exec_success(&self) -> u64 {
1491        self.exec_success
1492    }
1493
1494    #[must_use]
1495    pub const fn exec_error_corruption(&self) -> u64 {
1496        self.exec_error_corruption
1497    }
1498
1499    #[must_use]
1500    pub const fn exec_error_incompatible_persisted_format(&self) -> u64 {
1501        self.exec_error_incompatible_persisted_format
1502    }
1503
1504    #[must_use]
1505    pub const fn exec_error_not_found(&self) -> u64 {
1506        self.exec_error_not_found
1507    }
1508
1509    #[must_use]
1510    pub const fn exec_error_internal(&self) -> u64 {
1511        self.exec_error_internal
1512    }
1513
1514    #[must_use]
1515    pub const fn exec_error_conflict(&self) -> u64 {
1516        self.exec_error_conflict
1517    }
1518
1519    #[must_use]
1520    pub const fn exec_error_unsupported(&self) -> u64 {
1521        self.exec_error_unsupported
1522    }
1523
1524    #[must_use]
1525    pub const fn exec_error_invariant_violation(&self) -> u64 {
1526        self.exec_error_invariant_violation
1527    }
1528
1529    #[must_use]
1530    pub const fn exec_aborted(&self) -> u64 {
1531        self.exec_aborted
1532    }
1533
1534    #[must_use]
1535    pub const fn cache_shared_query_plan_hits(&self) -> u64 {
1536        self.cache_shared_query_plan_hits
1537    }
1538
1539    #[must_use]
1540    pub const fn cache_shared_query_plan_misses(&self) -> u64 {
1541        self.cache_shared_query_plan_misses
1542    }
1543
1544    #[must_use]
1545    pub const fn cache_shared_query_plan_inserts(&self) -> u64 {
1546        self.cache_shared_query_plan_inserts
1547    }
1548
1549    #[must_use]
1550    pub const fn cache_shared_query_plan_miss_cold(&self) -> u64 {
1551        self.cache_shared_query_plan_miss_cold
1552    }
1553
1554    #[must_use]
1555    pub const fn cache_shared_query_plan_miss_distinct_key(&self) -> u64 {
1556        self.cache_shared_query_plan_miss_distinct_key
1557    }
1558
1559    #[must_use]
1560    pub const fn cache_shared_query_plan_miss_method_version(&self) -> u64 {
1561        self.cache_shared_query_plan_miss_method_version
1562    }
1563
1564    #[must_use]
1565    pub const fn cache_shared_query_plan_miss_schema_fingerprint(&self) -> u64 {
1566        self.cache_shared_query_plan_miss_schema_fingerprint
1567    }
1568
1569    #[must_use]
1570    pub const fn cache_shared_query_plan_miss_visibility(&self) -> u64 {
1571        self.cache_shared_query_plan_miss_visibility
1572    }
1573
1574    #[must_use]
1575    pub const fn cache_sql_compiled_command_hits(&self) -> u64 {
1576        self.cache_sql_compiled_command_hits
1577    }
1578
1579    #[must_use]
1580    pub const fn cache_sql_compiled_command_misses(&self) -> u64 {
1581        self.cache_sql_compiled_command_misses
1582    }
1583
1584    #[must_use]
1585    pub const fn cache_sql_compiled_command_inserts(&self) -> u64 {
1586        self.cache_sql_compiled_command_inserts
1587    }
1588
1589    #[must_use]
1590    pub const fn cache_sql_compiled_command_miss_cold(&self) -> u64 {
1591        self.cache_sql_compiled_command_miss_cold
1592    }
1593
1594    #[must_use]
1595    pub const fn cache_sql_compiled_command_miss_distinct_key(&self) -> u64 {
1596        self.cache_sql_compiled_command_miss_distinct_key
1597    }
1598
1599    #[must_use]
1600    pub const fn cache_sql_compiled_command_miss_method_version(&self) -> u64 {
1601        self.cache_sql_compiled_command_miss_method_version
1602    }
1603
1604    #[must_use]
1605    pub const fn cache_sql_compiled_command_miss_schema_fingerprint(&self) -> u64 {
1606        self.cache_sql_compiled_command_miss_schema_fingerprint
1607    }
1608
1609    #[must_use]
1610    pub const fn cache_sql_compiled_command_miss_surface(&self) -> u64 {
1611        self.cache_sql_compiled_command_miss_surface
1612    }
1613
1614    #[must_use]
1615    pub const fn schema_reconcile_checks(&self) -> u64 {
1616        self.schema_reconcile_checks
1617    }
1618
1619    #[must_use]
1620    pub const fn schema_reconcile_exact_match(&self) -> u64 {
1621        self.schema_reconcile_exact_match
1622    }
1623
1624    #[must_use]
1625    pub const fn schema_reconcile_first_create(&self) -> u64 {
1626        self.schema_reconcile_first_create
1627    }
1628
1629    #[must_use]
1630    pub const fn schema_reconcile_latest_snapshot_corrupt(&self) -> u64 {
1631        self.schema_reconcile_latest_snapshot_corrupt
1632    }
1633
1634    #[must_use]
1635    pub const fn schema_reconcile_rejected_field_slot(&self) -> u64 {
1636        self.schema_reconcile_rejected_field_slot
1637    }
1638
1639    #[must_use]
1640    pub const fn schema_reconcile_rejected_other(&self) -> u64 {
1641        self.schema_reconcile_rejected_other
1642    }
1643
1644    #[must_use]
1645    pub const fn schema_reconcile_rejected_row_layout(&self) -> u64 {
1646        self.schema_reconcile_rejected_row_layout
1647    }
1648
1649    #[must_use]
1650    pub const fn schema_reconcile_rejected_schema_version(&self) -> u64 {
1651        self.schema_reconcile_rejected_schema_version
1652    }
1653
1654    #[must_use]
1655    pub const fn schema_reconcile_store_write_error(&self) -> u64 {
1656        self.schema_reconcile_store_write_error
1657    }
1658
1659    #[must_use]
1660    pub const fn schema_transition_checks(&self) -> u64 {
1661        self.schema_transition_checks
1662    }
1663
1664    #[must_use]
1665    pub const fn schema_transition_append_only_nullable_fields(&self) -> u64 {
1666        self.schema_transition_append_only_nullable_fields
1667    }
1668
1669    #[must_use]
1670    pub const fn schema_transition_exact_match(&self) -> u64 {
1671        self.schema_transition_exact_match
1672    }
1673
1674    #[must_use]
1675    pub const fn schema_transition_rejected_entity_identity(&self) -> u64 {
1676        self.schema_transition_rejected_entity_identity
1677    }
1678
1679    #[must_use]
1680    pub const fn schema_transition_rejected_field_contract(&self) -> u64 {
1681        self.schema_transition_rejected_field_contract
1682    }
1683
1684    #[must_use]
1685    pub const fn schema_transition_rejected_field_slot(&self) -> u64 {
1686        self.schema_transition_rejected_field_slot
1687    }
1688
1689    #[must_use]
1690    pub const fn schema_transition_rejected_row_layout(&self) -> u64 {
1691        self.schema_transition_rejected_row_layout
1692    }
1693
1694    #[must_use]
1695    pub const fn schema_transition_rejected_schema_version(&self) -> u64 {
1696        self.schema_transition_rejected_schema_version
1697    }
1698
1699    #[must_use]
1700    pub const fn schema_transition_rejected_snapshot(&self) -> u64 {
1701        self.schema_transition_rejected_snapshot
1702    }
1703
1704    #[must_use]
1705    pub const fn schema_store_snapshots(&self) -> u64 {
1706        self.schema_store_snapshots
1707    }
1708
1709    #[must_use]
1710    pub const fn schema_store_encoded_bytes(&self) -> u64 {
1711        self.schema_store_encoded_bytes
1712    }
1713
1714    #[must_use]
1715    pub const fn schema_store_latest_snapshot_bytes(&self) -> u64 {
1716        self.schema_store_latest_snapshot_bytes
1717    }
1718
1719    #[must_use]
1720    pub const fn accepted_schema_fields(&self) -> u64 {
1721        self.accepted_schema_fields
1722    }
1723
1724    #[must_use]
1725    pub const fn accepted_schema_nested_leaf_facts(&self) -> u64 {
1726        self.accepted_schema_nested_leaf_facts
1727    }
1728
1729    #[must_use]
1730    pub const fn sql_compile_rejects(&self) -> u64 {
1731        self.sql_compile_rejects
1732    }
1733
1734    #[must_use]
1735    pub const fn sql_compile_reject_cache_key(&self) -> u64 {
1736        self.sql_compile_reject_cache_key
1737    }
1738
1739    #[must_use]
1740    pub const fn sql_compile_reject_parse(&self) -> u64 {
1741        self.sql_compile_reject_parse
1742    }
1743
1744    #[must_use]
1745    pub const fn sql_compile_reject_semantic(&self) -> u64 {
1746        self.sql_compile_reject_semantic
1747    }
1748
1749    #[must_use]
1750    pub const fn plan_index(&self) -> u64 {
1751        self.plan_index
1752    }
1753
1754    #[must_use]
1755    pub const fn plan_keys(&self) -> u64 {
1756        self.plan_keys
1757    }
1758
1759    #[must_use]
1760    pub const fn plan_range(&self) -> u64 {
1761        self.plan_range
1762    }
1763
1764    #[must_use]
1765    pub const fn plan_full_scan(&self) -> u64 {
1766        self.plan_full_scan
1767    }
1768
1769    #[must_use]
1770    pub const fn plan_by_key(&self) -> u64 {
1771        self.plan_by_key
1772    }
1773
1774    #[must_use]
1775    pub const fn plan_by_keys(&self) -> u64 {
1776        self.plan_by_keys
1777    }
1778
1779    #[must_use]
1780    pub const fn plan_key_range(&self) -> u64 {
1781        self.plan_key_range
1782    }
1783
1784    #[must_use]
1785    pub const fn plan_index_prefix(&self) -> u64 {
1786        self.plan_index_prefix
1787    }
1788
1789    #[must_use]
1790    pub const fn plan_index_multi_lookup(&self) -> u64 {
1791        self.plan_index_multi_lookup
1792    }
1793
1794    #[must_use]
1795    pub const fn plan_index_range(&self) -> u64 {
1796        self.plan_index_range
1797    }
1798
1799    #[must_use]
1800    pub const fn plan_explicit_full_scan(&self) -> u64 {
1801        self.plan_explicit_full_scan
1802    }
1803
1804    #[must_use]
1805    pub const fn plan_union(&self) -> u64 {
1806        self.plan_union
1807    }
1808
1809    #[must_use]
1810    pub const fn plan_intersection(&self) -> u64 {
1811        self.plan_intersection
1812    }
1813
1814    #[must_use]
1815    pub const fn plan_grouped_hash_materialized(&self) -> u64 {
1816        self.plan_grouped_hash_materialized
1817    }
1818
1819    #[must_use]
1820    pub const fn plan_grouped_ordered_materialized(&self) -> u64 {
1821        self.plan_grouped_ordered_materialized
1822    }
1823
1824    #[must_use]
1825    pub const fn plan_choice_conflicting_primary_key_children_access_preferred(&self) -> u64 {
1826        self.plan_choice_conflicting_primary_key_children_access_preferred
1827    }
1828
1829    #[must_use]
1830    pub const fn plan_choice_constant_false_predicate(&self) -> u64 {
1831        self.plan_choice_constant_false_predicate
1832    }
1833
1834    #[must_use]
1835    pub const fn plan_choice_empty_child_access_preferred(&self) -> u64 {
1836        self.plan_choice_empty_child_access_preferred
1837    }
1838
1839    #[must_use]
1840    pub const fn plan_choice_full_scan_access(&self) -> u64 {
1841        self.plan_choice_full_scan_access
1842    }
1843
1844    #[must_use]
1845    pub const fn plan_choice_intent_key_access_override(&self) -> u64 {
1846        self.plan_choice_intent_key_access_override
1847    }
1848
1849    #[must_use]
1850    pub const fn plan_choice_limit_zero_window(&self) -> u64 {
1851        self.plan_choice_limit_zero_window
1852    }
1853
1854    #[must_use]
1855    pub const fn plan_choice_non_index_access(&self) -> u64 {
1856        self.plan_choice_non_index_access
1857    }
1858
1859    #[must_use]
1860    pub const fn plan_choice_planner_composite_non_index(&self) -> u64 {
1861        self.plan_choice_planner_composite_non_index
1862    }
1863
1864    #[must_use]
1865    pub const fn plan_choice_planner_full_scan_fallback(&self) -> u64 {
1866        self.plan_choice_planner_full_scan_fallback
1867    }
1868
1869    #[must_use]
1870    pub const fn plan_choice_planner_key_set_access(&self) -> u64 {
1871        self.plan_choice_planner_key_set_access
1872    }
1873
1874    #[must_use]
1875    pub const fn plan_choice_planner_primary_key_lookup(&self) -> u64 {
1876        self.plan_choice_planner_primary_key_lookup
1877    }
1878
1879    #[must_use]
1880    pub const fn plan_choice_planner_primary_key_range(&self) -> u64 {
1881        self.plan_choice_planner_primary_key_range
1882    }
1883
1884    #[must_use]
1885    pub const fn plan_choice_required_order_primary_key_range_preferred(&self) -> u64 {
1886        self.plan_choice_required_order_primary_key_range_preferred
1887    }
1888
1889    #[must_use]
1890    pub const fn plan_choice_singleton_primary_key_child_access_preferred(&self) -> u64 {
1891        self.plan_choice_singleton_primary_key_child_access_preferred
1892    }
1893
1894    #[must_use]
1895    pub const fn prepared_shape_already_finalized(&self) -> u64 {
1896        self.prepared_shape_already_finalized
1897    }
1898
1899    #[must_use]
1900    pub const fn prepared_shape_generated_fallback(&self) -> u64 {
1901        self.prepared_shape_generated_fallback
1902    }
1903
1904    #[must_use]
1905    pub const fn rows_loaded(&self) -> u64 {
1906        self.rows_loaded
1907    }
1908
1909    #[must_use]
1910    pub const fn rows_saved(&self) -> u64 {
1911        self.rows_saved
1912    }
1913
1914    #[must_use]
1915    pub const fn rows_inserted(&self) -> u64 {
1916        self.rows_inserted
1917    }
1918
1919    #[must_use]
1920    pub const fn rows_updated(&self) -> u64 {
1921        self.rows_updated
1922    }
1923
1924    #[must_use]
1925    pub const fn rows_replaced(&self) -> u64 {
1926        self.rows_replaced
1927    }
1928
1929    #[must_use]
1930    pub const fn rows_scanned(&self) -> u64 {
1931        self.rows_scanned
1932    }
1933
1934    #[must_use]
1935    pub const fn rows_filtered(&self) -> u64 {
1936        self.rows_filtered
1937    }
1938
1939    #[must_use]
1940    pub const fn rows_aggregated(&self) -> u64 {
1941        self.rows_aggregated
1942    }
1943
1944    #[must_use]
1945    pub const fn rows_emitted(&self) -> u64 {
1946        self.rows_emitted
1947    }
1948
1949    #[must_use]
1950    pub const fn load_candidate_rows_scanned(&self) -> u64 {
1951        self.load_candidate_rows_scanned
1952    }
1953
1954    #[must_use]
1955    pub const fn load_candidate_rows_filtered(&self) -> u64 {
1956        self.load_candidate_rows_filtered
1957    }
1958
1959    #[must_use]
1960    pub const fn load_result_rows_emitted(&self) -> u64 {
1961        self.load_result_rows_emitted
1962    }
1963
1964    #[must_use]
1965    pub const fn rows_deleted(&self) -> u64 {
1966        self.rows_deleted
1967    }
1968
1969    #[must_use]
1970    pub const fn sql_insert_calls(&self) -> u64 {
1971        self.sql_insert_calls
1972    }
1973
1974    #[must_use]
1975    pub const fn sql_insert_select_calls(&self) -> u64 {
1976        self.sql_insert_select_calls
1977    }
1978
1979    #[must_use]
1980    pub const fn sql_update_calls(&self) -> u64 {
1981        self.sql_update_calls
1982    }
1983
1984    #[must_use]
1985    pub const fn sql_delete_calls(&self) -> u64 {
1986        self.sql_delete_calls
1987    }
1988
1989    #[must_use]
1990    pub const fn sql_write_matched_rows(&self) -> u64 {
1991        self.sql_write_matched_rows
1992    }
1993
1994    #[must_use]
1995    pub const fn sql_write_mutated_rows(&self) -> u64 {
1996        self.sql_write_mutated_rows
1997    }
1998
1999    #[must_use]
2000    pub const fn sql_write_returning_rows(&self) -> u64 {
2001        self.sql_write_returning_rows
2002    }
2003
2004    #[must_use]
2005    pub const fn sql_write_error_insert(&self) -> u64 {
2006        self.sql_write_error_insert
2007    }
2008
2009    #[must_use]
2010    pub const fn sql_write_error_insert_select(&self) -> u64 {
2011        self.sql_write_error_insert_select
2012    }
2013
2014    #[must_use]
2015    pub const fn sql_write_error_update(&self) -> u64 {
2016        self.sql_write_error_update
2017    }
2018
2019    #[must_use]
2020    pub const fn sql_write_error_delete(&self) -> u64 {
2021        self.sql_write_error_delete
2022    }
2023
2024    #[must_use]
2025    pub const fn sql_write_error_corruption(&self) -> u64 {
2026        self.sql_write_error_corruption
2027    }
2028
2029    #[must_use]
2030    pub const fn sql_write_error_incompatible_persisted_format(&self) -> u64 {
2031        self.sql_write_error_incompatible_persisted_format
2032    }
2033
2034    #[must_use]
2035    pub const fn sql_write_error_not_found(&self) -> u64 {
2036        self.sql_write_error_not_found
2037    }
2038
2039    #[must_use]
2040    pub const fn sql_write_error_internal(&self) -> u64 {
2041        self.sql_write_error_internal
2042    }
2043
2044    #[must_use]
2045    pub const fn sql_write_error_conflict(&self) -> u64 {
2046        self.sql_write_error_conflict
2047    }
2048
2049    #[must_use]
2050    pub const fn sql_write_error_unsupported(&self) -> u64 {
2051        self.sql_write_error_unsupported
2052    }
2053
2054    #[must_use]
2055    pub const fn sql_write_error_invariant_violation(&self) -> u64 {
2056        self.sql_write_error_invariant_violation
2057    }
2058
2059    #[must_use]
2060    pub const fn index_inserts(&self) -> u64 {
2061        self.index_inserts
2062    }
2063
2064    #[must_use]
2065    pub const fn index_removes(&self) -> u64 {
2066        self.index_removes
2067    }
2068
2069    #[must_use]
2070    pub const fn reverse_index_inserts(&self) -> u64 {
2071        self.reverse_index_inserts
2072    }
2073
2074    #[must_use]
2075    pub const fn reverse_index_removes(&self) -> u64 {
2076        self.reverse_index_removes
2077    }
2078
2079    #[must_use]
2080    pub const fn relation_reverse_lookups(&self) -> u64 {
2081        self.relation_reverse_lookups
2082    }
2083
2084    #[must_use]
2085    pub const fn relation_delete_blocks(&self) -> u64 {
2086        self.relation_delete_blocks
2087    }
2088
2089    #[must_use]
2090    pub const fn write_rows_touched(&self) -> u64 {
2091        self.write_rows_touched
2092    }
2093
2094    #[must_use]
2095    pub const fn write_index_entries_changed(&self) -> u64 {
2096        self.write_index_entries_changed
2097    }
2098
2099    #[must_use]
2100    pub const fn write_reverse_index_entries_changed(&self) -> u64 {
2101        self.write_reverse_index_entries_changed
2102    }
2103
2104    #[must_use]
2105    pub const fn write_relation_checks(&self) -> u64 {
2106        self.write_relation_checks
2107    }
2108
2109    #[must_use]
2110    pub const fn unique_violations(&self) -> u64 {
2111        self.unique_violations
2112    }
2113
2114    #[must_use]
2115    pub const fn non_atomic_partial_commits(&self) -> u64 {
2116        self.non_atomic_partial_commits
2117    }
2118
2119    #[must_use]
2120    pub const fn non_atomic_partial_rows_committed(&self) -> u64 {
2121        self.non_atomic_partial_rows_committed
2122    }
2123
2124    /// Returns result rows emitted per load candidate row scanned.
2125    #[must_use]
2126    pub const fn load_selectivity_ratio(&self) -> Option<MetricRatio> {
2127        ratio(
2128            self.load_result_rows_emitted,
2129            self.load_candidate_rows_scanned,
2130        )
2131    }
2132
2133    /// Returns candidate rows filtered per load candidate row scanned.
2134    #[must_use]
2135    pub const fn load_filter_ratio(&self) -> Option<MetricRatio> {
2136        ratio(
2137            self.load_candidate_rows_filtered,
2138            self.load_candidate_rows_scanned,
2139        )
2140    }
2141
2142    /// Returns SQL-mutated rows per SQL-matched row.
2143    #[must_use]
2144    pub const fn sql_write_mutation_ratio(&self) -> Option<MetricRatio> {
2145        ratio(self.sql_write_mutated_rows, self.sql_write_matched_rows)
2146    }
2147
2148    /// Returns SQL `RETURNING` rows per SQL-mutated row.
2149    #[must_use]
2150    pub const fn sql_write_returning_ratio(&self) -> Option<MetricRatio> {
2151        ratio(self.sql_write_returning_rows, self.sql_write_mutated_rows)
2152    }
2153
2154    /// Returns primary index entries changed per write row touched.
2155    #[must_use]
2156    pub const fn write_index_entries_per_row(&self) -> Option<MetricRatio> {
2157        ratio(self.write_index_entries_changed, self.write_rows_touched)
2158    }
2159
2160    /// Returns reverse-index entries changed per write row touched.
2161    #[must_use]
2162    pub const fn write_reverse_index_entries_per_row(&self) -> Option<MetricRatio> {
2163        ratio(
2164            self.write_reverse_index_entries_changed,
2165            self.write_rows_touched,
2166        )
2167    }
2168
2169    /// Returns relation checks performed per write row touched.
2170    #[must_use]
2171    pub const fn write_relation_checks_per_row(&self) -> Option<MetricRatio> {
2172        ratio(self.write_relation_checks, self.write_rows_touched)
2173    }
2174
2175    // Rank entity summaries by all visible activity so write-heavy or
2176    // maintenance-heavy entities are not hidden below read-heavy entities.
2177    #[expect(clippy::too_many_lines)]
2178    const fn activity_score(&self) -> u64 {
2179        self.load_calls
2180            .saturating_add(self.save_calls)
2181            .saturating_add(self.delete_calls)
2182            .saturating_add(self.save_insert_calls)
2183            .saturating_add(self.save_update_calls)
2184            .saturating_add(self.save_replace_calls)
2185            .saturating_add(self.exec_success)
2186            .saturating_add(self.exec_error_corruption)
2187            .saturating_add(self.exec_error_incompatible_persisted_format)
2188            .saturating_add(self.exec_error_not_found)
2189            .saturating_add(self.exec_error_internal)
2190            .saturating_add(self.exec_error_conflict)
2191            .saturating_add(self.exec_error_unsupported)
2192            .saturating_add(self.exec_error_invariant_violation)
2193            .saturating_add(self.exec_aborted)
2194            .saturating_add(self.cache_shared_query_plan_hits)
2195            .saturating_add(self.cache_shared_query_plan_misses)
2196            .saturating_add(self.cache_shared_query_plan_inserts)
2197            .saturating_add(self.cache_shared_query_plan_miss_cold)
2198            .saturating_add(self.cache_shared_query_plan_miss_distinct_key)
2199            .saturating_add(self.cache_shared_query_plan_miss_method_version)
2200            .saturating_add(self.cache_shared_query_plan_miss_schema_fingerprint)
2201            .saturating_add(self.cache_shared_query_plan_miss_visibility)
2202            .saturating_add(self.cache_sql_compiled_command_hits)
2203            .saturating_add(self.cache_sql_compiled_command_misses)
2204            .saturating_add(self.cache_sql_compiled_command_inserts)
2205            .saturating_add(self.cache_sql_compiled_command_miss_cold)
2206            .saturating_add(self.cache_sql_compiled_command_miss_distinct_key)
2207            .saturating_add(self.cache_sql_compiled_command_miss_method_version)
2208            .saturating_add(self.cache_sql_compiled_command_miss_schema_fingerprint)
2209            .saturating_add(self.cache_sql_compiled_command_miss_surface)
2210            .saturating_add(self.schema_reconcile_checks)
2211            .saturating_add(self.schema_reconcile_exact_match)
2212            .saturating_add(self.schema_reconcile_first_create)
2213            .saturating_add(self.schema_reconcile_latest_snapshot_corrupt)
2214            .saturating_add(self.schema_reconcile_rejected_field_slot)
2215            .saturating_add(self.schema_reconcile_rejected_other)
2216            .saturating_add(self.schema_reconcile_rejected_row_layout)
2217            .saturating_add(self.schema_reconcile_rejected_schema_version)
2218            .saturating_add(self.schema_reconcile_store_write_error)
2219            .saturating_add(self.schema_transition_checks)
2220            .saturating_add(self.schema_transition_append_only_nullable_fields)
2221            .saturating_add(self.schema_transition_exact_match)
2222            .saturating_add(self.schema_transition_rejected_entity_identity)
2223            .saturating_add(self.schema_transition_rejected_field_contract)
2224            .saturating_add(self.schema_transition_rejected_field_slot)
2225            .saturating_add(self.schema_transition_rejected_row_layout)
2226            .saturating_add(self.schema_transition_rejected_schema_version)
2227            .saturating_add(self.schema_transition_rejected_snapshot)
2228            .saturating_add(self.schema_store_snapshots)
2229            .saturating_add(self.schema_store_encoded_bytes)
2230            .saturating_add(self.schema_store_latest_snapshot_bytes)
2231            .saturating_add(self.accepted_schema_fields)
2232            .saturating_add(self.accepted_schema_nested_leaf_facts)
2233            .saturating_add(self.sql_compile_rejects)
2234            .saturating_add(self.sql_compile_reject_cache_key)
2235            .saturating_add(self.sql_compile_reject_parse)
2236            .saturating_add(self.sql_compile_reject_semantic)
2237            .saturating_add(self.plan_index)
2238            .saturating_add(self.plan_keys)
2239            .saturating_add(self.plan_range)
2240            .saturating_add(self.plan_full_scan)
2241            .saturating_add(self.plan_by_key)
2242            .saturating_add(self.plan_by_keys)
2243            .saturating_add(self.plan_key_range)
2244            .saturating_add(self.plan_index_prefix)
2245            .saturating_add(self.plan_index_multi_lookup)
2246            .saturating_add(self.plan_index_range)
2247            .saturating_add(self.plan_explicit_full_scan)
2248            .saturating_add(self.plan_union)
2249            .saturating_add(self.plan_intersection)
2250            .saturating_add(self.plan_grouped_hash_materialized)
2251            .saturating_add(self.plan_grouped_ordered_materialized)
2252            .saturating_add(self.plan_choice_conflicting_primary_key_children_access_preferred)
2253            .saturating_add(self.plan_choice_constant_false_predicate)
2254            .saturating_add(self.plan_choice_empty_child_access_preferred)
2255            .saturating_add(self.plan_choice_full_scan_access)
2256            .saturating_add(self.plan_choice_intent_key_access_override)
2257            .saturating_add(self.plan_choice_limit_zero_window)
2258            .saturating_add(self.plan_choice_non_index_access)
2259            .saturating_add(self.plan_choice_planner_composite_non_index)
2260            .saturating_add(self.plan_choice_planner_full_scan_fallback)
2261            .saturating_add(self.plan_choice_planner_key_set_access)
2262            .saturating_add(self.plan_choice_planner_primary_key_lookup)
2263            .saturating_add(self.plan_choice_planner_primary_key_range)
2264            .saturating_add(self.plan_choice_required_order_primary_key_range_preferred)
2265            .saturating_add(self.plan_choice_singleton_primary_key_child_access_preferred)
2266            .saturating_add(self.prepared_shape_already_finalized)
2267            .saturating_add(self.prepared_shape_generated_fallback)
2268            .saturating_add(self.rows_loaded)
2269            .saturating_add(self.rows_saved)
2270            .saturating_add(self.rows_inserted)
2271            .saturating_add(self.rows_updated)
2272            .saturating_add(self.rows_replaced)
2273            .saturating_add(self.rows_scanned)
2274            .saturating_add(self.rows_filtered)
2275            .saturating_add(self.rows_aggregated)
2276            .saturating_add(self.rows_emitted)
2277            .saturating_add(self.load_candidate_rows_scanned)
2278            .saturating_add(self.load_candidate_rows_filtered)
2279            .saturating_add(self.load_result_rows_emitted)
2280            .saturating_add(self.rows_deleted)
2281            .saturating_add(self.sql_insert_calls)
2282            .saturating_add(self.sql_insert_select_calls)
2283            .saturating_add(self.sql_update_calls)
2284            .saturating_add(self.sql_delete_calls)
2285            .saturating_add(self.sql_write_matched_rows)
2286            .saturating_add(self.sql_write_mutated_rows)
2287            .saturating_add(self.sql_write_returning_rows)
2288            .saturating_add(self.sql_write_error_insert)
2289            .saturating_add(self.sql_write_error_insert_select)
2290            .saturating_add(self.sql_write_error_update)
2291            .saturating_add(self.sql_write_error_delete)
2292            .saturating_add(self.sql_write_error_corruption)
2293            .saturating_add(self.sql_write_error_incompatible_persisted_format)
2294            .saturating_add(self.sql_write_error_not_found)
2295            .saturating_add(self.sql_write_error_internal)
2296            .saturating_add(self.sql_write_error_conflict)
2297            .saturating_add(self.sql_write_error_unsupported)
2298            .saturating_add(self.sql_write_error_invariant_violation)
2299            .saturating_add(self.index_inserts)
2300            .saturating_add(self.index_removes)
2301            .saturating_add(self.reverse_index_inserts)
2302            .saturating_add(self.reverse_index_removes)
2303            .saturating_add(self.relation_reverse_lookups)
2304            .saturating_add(self.relation_delete_blocks)
2305            .saturating_add(self.write_rows_touched)
2306            .saturating_add(self.write_index_entries_changed)
2307            .saturating_add(self.write_reverse_index_entries_changed)
2308            .saturating_add(self.write_relation_checks)
2309            .saturating_add(self.unique_violations)
2310            .saturating_add(self.non_atomic_partial_commits)
2311            .saturating_add(self.non_atomic_partial_rows_committed)
2312    }
2313}
2314
2315// Project mutable per-entity counters into the stable report DTO.
2316//
2317// Keeping this projection out of `report_window_start` leaves the window
2318// filtering logic readable while still making every report field explicit.
2319#[expect(clippy::too_many_lines)]
2320fn entity_summary_from_counters(path: &str, ops: &EntityCounters) -> EntitySummary {
2321    EntitySummary {
2322        path: path.to_string(),
2323        load_calls: ops.load_calls,
2324        save_calls: ops.save_calls,
2325        delete_calls: ops.delete_calls,
2326        save_insert_calls: ops.save_insert_calls,
2327        save_update_calls: ops.save_update_calls,
2328        save_replace_calls: ops.save_replace_calls,
2329        exec_success: ops.exec_success,
2330        exec_error_corruption: ops.exec_error_corruption,
2331        exec_error_incompatible_persisted_format: ops.exec_error_incompatible_persisted_format,
2332        exec_error_not_found: ops.exec_error_not_found,
2333        exec_error_internal: ops.exec_error_internal,
2334        exec_error_conflict: ops.exec_error_conflict,
2335        exec_error_unsupported: ops.exec_error_unsupported,
2336        exec_error_invariant_violation: ops.exec_error_invariant_violation,
2337        exec_aborted: ops.exec_aborted,
2338        cache_shared_query_plan_hits: ops.cache_shared_query_plan_hits,
2339        cache_shared_query_plan_misses: ops.cache_shared_query_plan_misses,
2340        cache_shared_query_plan_inserts: ops.cache_shared_query_plan_inserts,
2341        cache_shared_query_plan_miss_cold: ops.cache_shared_query_plan_miss_cold,
2342        cache_shared_query_plan_miss_distinct_key: ops.cache_shared_query_plan_miss_distinct_key,
2343        cache_shared_query_plan_miss_method_version: ops
2344            .cache_shared_query_plan_miss_method_version,
2345        cache_shared_query_plan_miss_schema_fingerprint: ops
2346            .cache_shared_query_plan_miss_schema_fingerprint,
2347        cache_shared_query_plan_miss_visibility: ops.cache_shared_query_plan_miss_visibility,
2348        cache_sql_compiled_command_hits: ops.cache_sql_compiled_command_hits,
2349        cache_sql_compiled_command_misses: ops.cache_sql_compiled_command_misses,
2350        cache_sql_compiled_command_inserts: ops.cache_sql_compiled_command_inserts,
2351        cache_sql_compiled_command_miss_cold: ops.cache_sql_compiled_command_miss_cold,
2352        cache_sql_compiled_command_miss_distinct_key: ops
2353            .cache_sql_compiled_command_miss_distinct_key,
2354        cache_sql_compiled_command_miss_method_version: ops
2355            .cache_sql_compiled_command_miss_method_version,
2356        cache_sql_compiled_command_miss_schema_fingerprint: ops
2357            .cache_sql_compiled_command_miss_schema_fingerprint,
2358        cache_sql_compiled_command_miss_surface: ops.cache_sql_compiled_command_miss_surface,
2359        schema_reconcile_checks: ops.schema_reconcile_checks,
2360        schema_reconcile_exact_match: ops.schema_reconcile_exact_match,
2361        schema_reconcile_first_create: ops.schema_reconcile_first_create,
2362        schema_reconcile_latest_snapshot_corrupt: ops.schema_reconcile_latest_snapshot_corrupt,
2363        schema_reconcile_rejected_field_slot: ops.schema_reconcile_rejected_field_slot,
2364        schema_reconcile_rejected_other: ops.schema_reconcile_rejected_other,
2365        schema_reconcile_rejected_row_layout: ops.schema_reconcile_rejected_row_layout,
2366        schema_reconcile_rejected_schema_version: ops.schema_reconcile_rejected_schema_version,
2367        schema_reconcile_store_write_error: ops.schema_reconcile_store_write_error,
2368        schema_transition_checks: ops.schema_transition_checks,
2369        schema_transition_append_only_nullable_fields: ops
2370            .schema_transition_append_only_nullable_fields,
2371        schema_transition_exact_match: ops.schema_transition_exact_match,
2372        schema_transition_rejected_entity_identity: ops.schema_transition_rejected_entity_identity,
2373        schema_transition_rejected_field_contract: ops.schema_transition_rejected_field_contract,
2374        schema_transition_rejected_field_slot: ops.schema_transition_rejected_field_slot,
2375        schema_transition_rejected_row_layout: ops.schema_transition_rejected_row_layout,
2376        schema_transition_rejected_schema_version: ops.schema_transition_rejected_schema_version,
2377        schema_transition_rejected_snapshot: ops.schema_transition_rejected_snapshot,
2378        schema_store_snapshots: ops.schema_store_snapshots,
2379        schema_store_encoded_bytes: ops.schema_store_encoded_bytes,
2380        schema_store_latest_snapshot_bytes: ops.schema_store_latest_snapshot_bytes,
2381        accepted_schema_fields: ops.accepted_schema_fields,
2382        accepted_schema_nested_leaf_facts: ops.accepted_schema_nested_leaf_facts,
2383        sql_compile_rejects: ops.sql_compile_rejects,
2384        sql_compile_reject_cache_key: ops.sql_compile_reject_cache_key,
2385        sql_compile_reject_parse: ops.sql_compile_reject_parse,
2386        sql_compile_reject_semantic: ops.sql_compile_reject_semantic,
2387        plan_index: ops.plan_index,
2388        plan_keys: ops.plan_keys,
2389        plan_range: ops.plan_range,
2390        plan_full_scan: ops.plan_full_scan,
2391        plan_by_key: ops.plan_by_key,
2392        plan_by_keys: ops.plan_by_keys,
2393        plan_key_range: ops.plan_key_range,
2394        plan_index_prefix: ops.plan_index_prefix,
2395        plan_index_multi_lookup: ops.plan_index_multi_lookup,
2396        plan_index_range: ops.plan_index_range,
2397        plan_explicit_full_scan: ops.plan_explicit_full_scan,
2398        plan_union: ops.plan_union,
2399        plan_intersection: ops.plan_intersection,
2400        plan_grouped_hash_materialized: ops.plan_grouped_hash_materialized,
2401        plan_grouped_ordered_materialized: ops.plan_grouped_ordered_materialized,
2402        plan_choice_conflicting_primary_key_children_access_preferred: ops
2403            .plan_choice_conflicting_primary_key_children_access_preferred,
2404        plan_choice_constant_false_predicate: ops.plan_choice_constant_false_predicate,
2405        plan_choice_empty_child_access_preferred: ops.plan_choice_empty_child_access_preferred,
2406        plan_choice_full_scan_access: ops.plan_choice_full_scan_access,
2407        plan_choice_intent_key_access_override: ops.plan_choice_intent_key_access_override,
2408        plan_choice_limit_zero_window: ops.plan_choice_limit_zero_window,
2409        plan_choice_non_index_access: ops.plan_choice_non_index_access,
2410        plan_choice_planner_composite_non_index: ops.plan_choice_planner_composite_non_index,
2411        plan_choice_planner_full_scan_fallback: ops.plan_choice_planner_full_scan_fallback,
2412        plan_choice_planner_key_set_access: ops.plan_choice_planner_key_set_access,
2413        plan_choice_planner_primary_key_lookup: ops.plan_choice_planner_primary_key_lookup,
2414        plan_choice_planner_primary_key_range: ops.plan_choice_planner_primary_key_range,
2415        plan_choice_required_order_primary_key_range_preferred: ops
2416            .plan_choice_required_order_primary_key_range_preferred,
2417        plan_choice_singleton_primary_key_child_access_preferred: ops
2418            .plan_choice_singleton_primary_key_child_access_preferred,
2419        prepared_shape_already_finalized: ops.prepared_shape_already_finalized,
2420        prepared_shape_generated_fallback: ops.prepared_shape_generated_fallback,
2421        rows_loaded: ops.rows_loaded,
2422        rows_saved: ops.rows_saved,
2423        rows_inserted: ops.rows_inserted,
2424        rows_updated: ops.rows_updated,
2425        rows_replaced: ops.rows_replaced,
2426        rows_scanned: ops.rows_scanned,
2427        rows_filtered: ops.rows_filtered,
2428        rows_aggregated: ops.rows_aggregated,
2429        rows_emitted: ops.rows_emitted,
2430        load_candidate_rows_scanned: ops.load_candidate_rows_scanned,
2431        load_candidate_rows_filtered: ops.load_candidate_rows_filtered,
2432        load_result_rows_emitted: ops.load_result_rows_emitted,
2433        rows_deleted: ops.rows_deleted,
2434        sql_insert_calls: ops.sql_insert_calls,
2435        sql_insert_select_calls: ops.sql_insert_select_calls,
2436        sql_update_calls: ops.sql_update_calls,
2437        sql_delete_calls: ops.sql_delete_calls,
2438        sql_write_matched_rows: ops.sql_write_matched_rows,
2439        sql_write_mutated_rows: ops.sql_write_mutated_rows,
2440        sql_write_returning_rows: ops.sql_write_returning_rows,
2441        sql_write_error_insert: ops.sql_write_error_insert,
2442        sql_write_error_insert_select: ops.sql_write_error_insert_select,
2443        sql_write_error_update: ops.sql_write_error_update,
2444        sql_write_error_delete: ops.sql_write_error_delete,
2445        sql_write_error_corruption: ops.sql_write_error_corruption,
2446        sql_write_error_incompatible_persisted_format: ops
2447            .sql_write_error_incompatible_persisted_format,
2448        sql_write_error_not_found: ops.sql_write_error_not_found,
2449        sql_write_error_internal: ops.sql_write_error_internal,
2450        sql_write_error_conflict: ops.sql_write_error_conflict,
2451        sql_write_error_unsupported: ops.sql_write_error_unsupported,
2452        sql_write_error_invariant_violation: ops.sql_write_error_invariant_violation,
2453        index_inserts: ops.index_inserts,
2454        index_removes: ops.index_removes,
2455        reverse_index_inserts: ops.reverse_index_inserts,
2456        reverse_index_removes: ops.reverse_index_removes,
2457        relation_reverse_lookups: ops.relation_reverse_lookups,
2458        relation_delete_blocks: ops.relation_delete_blocks,
2459        write_rows_touched: ops.write_rows_touched,
2460        write_index_entries_changed: ops.write_index_entries_changed,
2461        write_reverse_index_entries_changed: ops.write_reverse_index_entries_changed,
2462        write_relation_checks: ops.write_relation_checks,
2463        unique_violations: ops.unique_violations,
2464        non_atomic_partial_commits: ops.non_atomic_partial_commits,
2465        non_atomic_partial_rows_committed: ops.non_atomic_partial_rows_committed,
2466    }
2467}
2468
2469// Build a metrics report gated by `window_start_ms`.
2470//
2471// This is a window-start filter:
2472// - If `window_start_ms` is `None`, return the current window.
2473// - If `window_start_ms <= state.window_start_ms`, return the current window.
2474// - If `window_start_ms > state.window_start_ms`, return an empty report.
2475//
2476// IcyDB stores aggregate counters only, so it cannot produce a precise
2477// sub-window report after `state.window_start_ms`.
2478#[must_use]
2479pub(super) fn report_window_start(window_start_ms: Option<u64>) -> EventReport {
2480    let snap = with_state(Clone::clone);
2481    if let Some(requested_window_start_ms) = window_start_ms
2482        && requested_window_start_ms > snap.window_start_ms
2483    {
2484        return EventReport::new(
2485            None,
2486            Vec::new(),
2487            false,
2488            window_start_ms,
2489            snap.window_start_ms,
2490        );
2491    }
2492
2493    let mut entity_counters: Vec<EntitySummary> = Vec::new();
2494    for (path, ops) in &snap.entities {
2495        entity_counters.push(entity_summary_from_counters(path, ops));
2496    }
2497
2498    entity_counters.sort_by(|a, b| {
2499        b.activity_score()
2500            .cmp(&a.activity_score())
2501            .then_with(|| b.rows_loaded.cmp(&a.rows_loaded))
2502            .then_with(|| b.rows_saved.cmp(&a.rows_saved))
2503            .then_with(|| b.rows_scanned.cmp(&a.rows_scanned))
2504            .then_with(|| b.rows_deleted.cmp(&a.rows_deleted))
2505            .then_with(|| a.path.cmp(&b.path))
2506    });
2507
2508    EventReport::new(
2509        Some(EventCounters::new(
2510            snap.ops.clone(),
2511            snap.perf.clone(),
2512            snap.window_start_ms,
2513            now_millis(),
2514        )),
2515        entity_counters,
2516        true,
2517        window_start_ms,
2518        snap.window_start_ms,
2519    )
2520}