rns_net/common/
interface_stats.rs1pub const ANNOUNCE_SAMPLE_MAX: usize = 48;
3
4pub const PATH_REQUEST_SAMPLE_MAX: usize = 48;
6
7pub const INCOMING_ANNOUNCE_MIN_SAMPLE: usize = 2;
9
10pub const INCOMING_PATH_REQUEST_MIN_SAMPLE: usize = 2;
12
13#[derive(Debug, Clone, Default)]
15pub struct InterfaceStats {
16 pub rxb: u64,
17 pub txb: u64,
18 pub rx_packets: u64,
19 pub tx_packets: u64,
20 pub cpu_load: Option<f64>,
21 pub mem_load: Option<f64>,
22 pub switch_id: Option<[u8; 4]>,
23 pub endpoint_id: Option<[u8; 8]>,
24 pub via_switch_id: Option<[u8; 4]>,
25 pub peers: Option<usize>,
26 pub started: f64,
27 pub ia_timestamps: Vec<f64>,
29 pub oa_timestamps: Vec<f64>,
31 pub ip_timestamps: Vec<f64>,
33 pub op_timestamps: Vec<f64>,
35}
36
37impl InterfaceStats {
38 pub fn record_incoming_announce(&mut self, now: f64) {
40 self.ia_timestamps.push(now);
41 if self.ia_timestamps.len() > ANNOUNCE_SAMPLE_MAX {
42 self.ia_timestamps.remove(0);
43 }
44 }
45
46 pub fn record_outgoing_announce(&mut self, now: f64) {
48 self.oa_timestamps.push(now);
49 if self.oa_timestamps.len() > ANNOUNCE_SAMPLE_MAX {
50 self.oa_timestamps.remove(0);
51 }
52 }
53
54 pub fn record_incoming_path_request(&mut self, now: f64) {
56 self.ip_timestamps.push(now);
57 if self.ip_timestamps.len() > PATH_REQUEST_SAMPLE_MAX {
58 self.ip_timestamps.remove(0);
59 }
60 }
61
62 pub fn record_outgoing_path_request(&mut self, now: f64) {
64 self.op_timestamps.push(now);
65 if self.op_timestamps.len() > PATH_REQUEST_SAMPLE_MAX {
66 self.op_timestamps.remove(0);
67 }
68 }
69
70 fn compute_frequency(timestamps: &[f64], min_sample: usize) -> f64 {
72 let sample_count = timestamps.len();
73 if sample_count <= min_sample {
74 return 0.0;
75 }
76 let span = timestamps[sample_count - 1] - timestamps[0];
77 if span <= 0.0 {
78 return 0.0;
79 }
80 sample_count as f64 / span
81 }
82
83 pub fn incoming_announce_freq(&self) -> f64 {
85 Self::compute_frequency(&self.ia_timestamps, INCOMING_ANNOUNCE_MIN_SAMPLE)
86 }
87
88 pub fn outgoing_announce_freq(&self) -> f64 {
90 Self::compute_frequency(&self.oa_timestamps, 1)
91 }
92
93 pub fn incoming_path_request_freq(&self) -> f64 {
95 Self::compute_frequency(&self.ip_timestamps, INCOMING_PATH_REQUEST_MIN_SAMPLE)
96 }
97
98 pub fn outgoing_path_request_freq(&self) -> f64 {
100 Self::compute_frequency(&self.op_timestamps, 1)
101 }
102
103 pub fn outgoing_path_request_samples(&self) -> usize {
105 self.op_timestamps.len()
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn incoming_frequency_waits_for_minimum_sample_count() {
115 let mut stats = InterfaceStats::default();
116
117 for i in 0..INCOMING_ANNOUNCE_MIN_SAMPLE {
118 stats.record_incoming_announce(i as f64);
119 }
120
121 assert_eq!(
122 stats.incoming_announce_freq(),
123 0.0,
124 "incoming announce frequency must stay zero until more than the minimum samples exist"
125 );
126 }
127
128 #[test]
129 fn announce_frequency_keeps_bounded_samples() {
130 let mut stats = InterfaceStats::default();
131
132 for i in 0..ANNOUNCE_SAMPLE_MAX {
133 stats.record_incoming_announce(i as f64);
134 stats.record_outgoing_announce(i as f64);
135 }
136
137 assert_eq!(stats.ia_timestamps.len(), ANNOUNCE_SAMPLE_MAX);
138 assert_eq!(stats.oa_timestamps.len(), ANNOUNCE_SAMPLE_MAX);
139
140 stats.record_incoming_announce(ANNOUNCE_SAMPLE_MAX as f64);
141 stats.record_outgoing_announce(ANNOUNCE_SAMPLE_MAX as f64);
142
143 assert_eq!(stats.ia_timestamps.len(), ANNOUNCE_SAMPLE_MAX);
144 assert_eq!(stats.oa_timestamps.len(), ANNOUNCE_SAMPLE_MAX);
145 assert_eq!(stats.ia_timestamps[0], 1.0);
146 assert_eq!(stats.oa_timestamps[0], 1.0);
147 }
148
149 #[test]
150 fn incoming_frequency_uses_sample_count_over_oldest_span() {
151 let mut stats = InterfaceStats::default();
152
153 for i in 0..12 {
154 stats.record_incoming_announce(i as f64);
155 }
156
157 let expected = 12.0 / 11.0;
158 assert!(
159 (stats.incoming_announce_freq() - expected).abs() < f64::EPSILON,
160 "incoming frequency should be samples / span, got {} expected {}",
161 stats.incoming_announce_freq(),
162 expected
163 );
164 }
165
166 #[test]
167 fn path_request_frequency_tracks_incoming_and_outgoing_samples() {
168 let mut stats = InterfaceStats::default();
169
170 for i in 0..=INCOMING_PATH_REQUEST_MIN_SAMPLE {
171 stats.record_incoming_path_request(i as f64);
172 }
173 stats.record_outgoing_path_request(10.0);
174 stats.record_outgoing_path_request(12.0);
175
176 assert_eq!(stats.incoming_path_request_freq(), 3.0 / 2.0);
177 assert_eq!(stats.outgoing_path_request_freq(), 2.0 / 2.0);
178 assert_eq!(stats.outgoing_path_request_samples(), 2);
179 }
180
181 #[test]
182 fn path_request_frequency_keeps_bounded_samples() {
183 let mut stats = InterfaceStats::default();
184
185 for i in 0..PATH_REQUEST_SAMPLE_MAX {
186 stats.record_incoming_path_request(i as f64);
187 stats.record_outgoing_path_request(i as f64);
188 }
189
190 stats.record_incoming_path_request(PATH_REQUEST_SAMPLE_MAX as f64);
191 stats.record_outgoing_path_request(PATH_REQUEST_SAMPLE_MAX as f64);
192
193 assert_eq!(stats.ip_timestamps.len(), PATH_REQUEST_SAMPLE_MAX);
194 assert_eq!(stats.op_timestamps.len(), PATH_REQUEST_SAMPLE_MAX);
195 assert_eq!(stats.ip_timestamps[0], 1.0);
196 assert_eq!(stats.op_timestamps[0], 1.0);
197 }
198}