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 `MeshRelayGovernor::is_active_relay()` at the moment of
37    /// emission: the standing is derived from traffic this device actually
38    /// carried, never predicted from thresholds.
39    /// Named deliberately — this is a local-role flag, not a peer count.
40    /// A future record with an explicit per-peer-relay registry may add a
41    /// `relay_peer_count` field alongside; those semantics are orthogonal.
42    pub is_local_relay: bool,
43    /// Currently selected transport, if any.
44    pub current_transport: Option<TransportType>,
45}
46
47impl MetricsFrame {
48    /// Stable OTel-friendly name for this record.
49    pub const NAME: &'static str = "protocol.metrics.snapshot";
50
51    /// Returns the stable telemetry name.
52    pub fn telemetry_name(&self) -> &'static str {
53        Self::NAME
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use offline_protocol_reliability::DeduplicatorMode;
61
62    fn sample_frame() -> MetricsFrame {
63        MetricsFrame {
64            timestamp_ms: 0,
65            transports: Vec::new(),
66            retry_queue: RetryQueueStats {
67                total_count: 0,
68                ready_count: 0,
69                critical_priority_count: 0,
70                high_priority_count: 0,
71                medium_priority_count: 0,
72                low_priority_count: 0,
73            },
74            dedup: DeduplicatorStats {
75                total_tracked: 0,
76                recent_tracked: 0,
77                capacity_used_percent: 0,
78                false_positive_rate: None,
79                mode: DeduplicatorMode::HashMap,
80            },
81            ack_pending: 0,
82            neighbor_count: 0,
83            is_local_relay: false,
84            current_transport: None,
85        }
86    }
87
88    #[test]
89    fn name_is_stable() {
90        assert_eq!(MetricsFrame::NAME, "protocol.metrics.snapshot");
91        assert_eq!(sample_frame().telemetry_name(), "protocol.metrics.snapshot");
92    }
93}