Skip to main content

offline_protocol/telemetry/
metrics_snapshot.rs

1//! Periodic snapshot of protocol-wide counters and per-transport metrics.
2//!
3//! Emitted on the cadence configured by
4//! [`crate::telemetry::TelemetryConfig::metrics_cadence`] from
5//! `OfflineProtocol::process()`. When cadence is `None`, no periodic emission
6//! occurs — apps fall back to pull-based access via `TransportManager::metrics()`.
7//!
8//! The frame reuses [`TransportMetrics`] verbatim; there is no parallel
9//! "rich" struct. Extending that struct (when UniFFI bindings catch up)
10//! automatically widens the frame.
11
12use offline_protocol_reliability::{DeduplicatorStats, RetryQueueStats};
13use offline_protocol_transport::{TransportMetrics, TransportType};
14
15/// Snapshot of every push-able counter the SDK exposes.
16///
17/// The inner collections are pre-materialized at aggregation time so the sink
18/// receives owned-but-cheap data — no iteration, no locking on the sink side.
19#[derive(Debug, Clone)]
20pub struct MetricsFrame {
21    /// Emission timestamp (milliseconds since Unix epoch).
22    pub timestamp_ms: i64,
23    /// Per-transport metrics for every transport currently reporting
24    /// `TransportStatus::Available`.
25    pub transports: Vec<(TransportType, TransportMetrics)>,
26    /// Retry-queue statistics (pending + ready + per-priority counts).
27    pub retry_queue: RetryQueueStats,
28    /// Deduplicator statistics (tracked ids, capacity, mode).
29    pub dedup: DeduplicatorStats,
30    /// Number of messages awaiting acknowledgement.
31    pub ack_pending: usize,
32    /// Number of currently known neighbors.
33    pub neighbor_count: usize,
34    /// Whether this node is currently acting as a relay on the mesh.
35    ///
36    /// Reflects `RelayManager::current_role()` at the moment of emission.
37    /// Named deliberately — this is a local-role flag, not a peer count.
38    /// A future record with an explicit per-peer-relay registry may add a
39    /// `relay_peer_count` field alongside; those semantics are orthogonal.
40    pub is_local_relay: bool,
41    /// Currently selected transport, if any.
42    pub current_transport: Option<TransportType>,
43}
44
45impl MetricsFrame {
46    /// Stable OTel-friendly name for this record.
47    pub const NAME: &'static str = "protocol.metrics.snapshot";
48
49    /// Returns the stable telemetry name.
50    pub fn telemetry_name(&self) -> &'static str {
51        Self::NAME
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use offline_protocol_reliability::DeduplicatorMode;
59
60    fn sample_frame() -> MetricsFrame {
61        MetricsFrame {
62            timestamp_ms: 0,
63            transports: Vec::new(),
64            retry_queue: RetryQueueStats {
65                total_count: 0,
66                ready_count: 0,
67                critical_priority_count: 0,
68                high_priority_count: 0,
69                medium_priority_count: 0,
70                low_priority_count: 0,
71            },
72            dedup: DeduplicatorStats {
73                total_tracked: 0,
74                recent_tracked: 0,
75                capacity_used_percent: 0,
76                false_positive_rate: None,
77                mode: DeduplicatorMode::HashMap,
78            },
79            ack_pending: 0,
80            neighbor_count: 0,
81            is_local_relay: false,
82            current_transport: None,
83        }
84    }
85
86    #[test]
87    fn name_is_stable() {
88        assert_eq!(MetricsFrame::NAME, "protocol.metrics.snapshot");
89        assert_eq!(sample_frame().telemetry_name(), "protocol.metrics.snapshot");
90    }
91}