use candid::CandidType;
use serde::Deserialize;
use crate::runtime::now_millis;
use super::{EntitySummary, EventOps, EventPerf, entity_summary_from_counters, with_state};
#[cfg_attr(doc, doc = "EventReport\n\nMetrics query payload.")]
#[derive(CandidType, Clone, Debug, Default, Deserialize)]
pub struct EventReport {
counters: Option<EventCounters>,
entity_counters: Vec<EntitySummary>,
window_filter_matched: bool,
requested_window_start_ms: Option<u64>,
active_window_start_ms: u64,
}
impl EventReport {
#[must_use]
const fn new(
counters: Option<EventCounters>,
entity_counters: Vec<EntitySummary>,
window_filter_matched: bool,
requested_window_start_ms: Option<u64>,
active_window_start_ms: u64,
) -> Self {
Self {
counters,
entity_counters,
window_filter_matched,
requested_window_start_ms,
active_window_start_ms,
}
}
#[must_use]
pub const fn counters(&self) -> Option<&EventCounters> {
self.counters.as_ref()
}
#[must_use]
pub fn entity_counters(&self) -> &[EntitySummary] {
&self.entity_counters
}
#[must_use]
pub const fn window_filter_matched(&self) -> bool {
self.window_filter_matched
}
#[must_use]
pub const fn requested_window_start_ms(&self) -> Option<u64> {
self.requested_window_start_ms
}
#[must_use]
pub const fn active_window_start_ms(&self) -> u64 {
self.active_window_start_ms
}
#[must_use]
pub fn into_counters(self) -> Option<EventCounters> {
self.counters
}
#[must_use]
pub fn into_entity_counters(self) -> Vec<EntitySummary> {
self.entity_counters
}
}
#[derive(CandidType, Clone, Debug, Default, Deserialize)]
pub struct EventCounters {
pub(crate) ops: EventOps,
pub(crate) perf: EventPerf,
pub(crate) window_start_ms: u64,
pub(crate) window_end_ms: u64,
pub(crate) window_duration_ms: u64,
}
impl EventCounters {
#[must_use]
const fn new(ops: EventOps, perf: EventPerf, window_start_ms: u64, window_end_ms: u64) -> Self {
Self {
ops,
perf,
window_start_ms,
window_end_ms,
window_duration_ms: window_end_ms.saturating_sub(window_start_ms),
}
}
#[must_use]
pub const fn ops(&self) -> &EventOps {
&self.ops
}
#[must_use]
pub const fn perf(&self) -> &EventPerf {
&self.perf
}
#[must_use]
pub const fn window_start_ms(&self) -> u64 {
self.window_start_ms
}
#[must_use]
pub const fn window_end_ms(&self) -> u64 {
self.window_end_ms
}
#[must_use]
pub const fn window_duration_ms(&self) -> u64 {
self.window_duration_ms
}
}
#[must_use]
pub(in crate::metrics) fn report_window_start(window_start_ms: Option<u64>) -> EventReport {
let snap = with_state(Clone::clone);
if let Some(requested_window_start_ms) = window_start_ms
&& requested_window_start_ms > snap.window_start_ms
{
return EventReport::new(
None,
Vec::new(),
false,
window_start_ms,
snap.window_start_ms,
);
}
let mut entity_counters: Vec<EntitySummary> = Vec::new();
for (path, ops) in &snap.entities {
entity_counters.push(entity_summary_from_counters(path, ops));
}
entity_counters.sort_by(|a, b| {
b.activity_score()
.cmp(&a.activity_score())
.then_with(|| b.rows_loaded().cmp(&a.rows_loaded()))
.then_with(|| b.rows_saved().cmp(&a.rows_saved()))
.then_with(|| b.rows_scanned().cmp(&a.rows_scanned()))
.then_with(|| b.rows_deleted().cmp(&a.rows_deleted()))
.then_with(|| a.path().cmp(b.path()))
});
EventReport::new(
Some(EventCounters::new(
snap.ops.clone(),
snap.perf.clone(),
snap.window_start_ms,
now_millis(),
)),
entity_counters,
true,
window_start_ms,
snap.window_start_ms,
)
}