icydb_core/metrics/state/
report.rs1use candid::CandidType;
7use serde::Deserialize;
8
9use crate::runtime::now_millis;
10
11use super::{EntitySummary, EventOps, EventPerf, entity_summary_from_counters, with_state};
12
13#[cfg_attr(doc, doc = "EventReport\n\nMetrics query payload.")]
14#[derive(CandidType, Clone, Debug, Default, Deserialize)]
15pub struct EventReport {
16 counters: Option<EventCounters>,
17 entity_counters: Vec<EntitySummary>,
18 window_filter_matched: bool,
19 requested_window_start_ms: Option<u64>,
20 active_window_start_ms: u64,
21}
22
23impl EventReport {
24 #[must_use]
25 const fn new(
26 counters: Option<EventCounters>,
27 entity_counters: Vec<EntitySummary>,
28 window_filter_matched: bool,
29 requested_window_start_ms: Option<u64>,
30 active_window_start_ms: u64,
31 ) -> Self {
32 Self {
33 counters,
34 entity_counters,
35 window_filter_matched,
36 requested_window_start_ms,
37 active_window_start_ms,
38 }
39 }
40
41 #[must_use]
42 pub const fn counters(&self) -> Option<&EventCounters> {
43 self.counters.as_ref()
44 }
45
46 #[must_use]
47 pub fn entity_counters(&self) -> &[EntitySummary] {
48 &self.entity_counters
49 }
50
51 #[must_use]
52 pub const fn window_filter_matched(&self) -> bool {
53 self.window_filter_matched
54 }
55
56 #[must_use]
57 pub const fn requested_window_start_ms(&self) -> Option<u64> {
58 self.requested_window_start_ms
59 }
60
61 #[must_use]
62 pub const fn active_window_start_ms(&self) -> u64 {
63 self.active_window_start_ms
64 }
65
66 #[must_use]
67 pub fn into_counters(self) -> Option<EventCounters> {
68 self.counters
69 }
70
71 #[must_use]
72 pub fn into_entity_counters(self) -> Vec<EntitySummary> {
73 self.entity_counters
74 }
75}
76
77#[derive(CandidType, Clone, Debug, Default, Deserialize)]
86pub struct EventCounters {
87 pub(crate) ops: EventOps,
88 pub(crate) perf: EventPerf,
89 pub(crate) window_start_ms: u64,
90 pub(crate) window_end_ms: u64,
91 pub(crate) window_duration_ms: u64,
92}
93
94impl EventCounters {
95 #[must_use]
96 const fn new(ops: EventOps, perf: EventPerf, window_start_ms: u64, window_end_ms: u64) -> Self {
97 Self {
98 ops,
99 perf,
100 window_start_ms,
101 window_end_ms,
102 window_duration_ms: window_end_ms.saturating_sub(window_start_ms),
103 }
104 }
105
106 #[must_use]
107 pub const fn ops(&self) -> &EventOps {
108 &self.ops
109 }
110
111 #[must_use]
112 pub const fn perf(&self) -> &EventPerf {
113 &self.perf
114 }
115
116 #[must_use]
117 pub const fn window_start_ms(&self) -> u64 {
118 self.window_start_ms
119 }
120
121 #[must_use]
122 pub const fn window_end_ms(&self) -> u64 {
123 self.window_end_ms
124 }
125
126 #[must_use]
127 pub const fn window_duration_ms(&self) -> u64 {
128 self.window_duration_ms
129 }
130}
131
132#[must_use]
142pub(in crate::metrics) fn report_window_start(window_start_ms: Option<u64>) -> EventReport {
143 let snap = with_state(Clone::clone);
144 if let Some(requested_window_start_ms) = window_start_ms
145 && requested_window_start_ms > snap.window_start_ms
146 {
147 return EventReport::new(
148 None,
149 Vec::new(),
150 false,
151 window_start_ms,
152 snap.window_start_ms,
153 );
154 }
155
156 let mut entity_counters: Vec<EntitySummary> = Vec::new();
157 for (path, ops) in &snap.entities {
158 entity_counters.push(entity_summary_from_counters(path, ops));
159 }
160
161 entity_counters.sort_by(|a, b| {
162 b.activity_score()
163 .cmp(&a.activity_score())
164 .then_with(|| b.rows_loaded().cmp(&a.rows_loaded()))
165 .then_with(|| b.rows_saved().cmp(&a.rows_saved()))
166 .then_with(|| b.rows_scanned().cmp(&a.rows_scanned()))
167 .then_with(|| b.rows_deleted().cmp(&a.rows_deleted()))
168 .then_with(|| a.path().cmp(b.path()))
169 });
170
171 EventReport::new(
172 Some(EventCounters::new(
173 snap.ops.clone(),
174 snap.perf.clone(),
175 snap.window_start_ms,
176 now_millis(),
177 )),
178 entity_counters,
179 true,
180 window_start_ms,
181 snap.window_start_ms,
182 )
183}