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}