1#![warn(missing_docs)]
2
3use std::sync::Arc;
6use std::sync::Mutex;
7
8use async_trait::async_trait;
9use chrono::DateTime;
10use chrono::Duration;
11use chrono::Utc;
12use dashmap::mapref::one::RefMut;
13use dashmap::DashMap;
14use rings_core::dht::Did;
15use rings_core::measure;
16use rings_core::measure::Measure;
17use rings_core::measure::MeasureCounter;
18use rings_core::measure::PeerQuality;
19use rings_core::measure::PeerQualityEvidence;
20use rings_core::measure::PeerQualityThresholds;
21use rings_core::storage::KvStorageInterface;
22use rings_derive::MeasureBehaviour;
23
24#[cfg(test)]
25const DURATION: u64 = 1;
26#[cfg(not(test))]
27const DURATION: u64 = 60 * 60;
28
29#[cfg(feature = "browser")]
32pub type MeasureStorage = Box<dyn KvStorageInterface<u64>>;
33
34#[cfg(not(feature = "browser"))]
37pub type MeasureStorage = Box<dyn KvStorageInterface<u64> + Sync + Send>;
38
39#[derive(MeasureBehaviour)]
43pub struct PeriodicMeasure {
44 storage: MeasureStorage,
45 counters: DashMap<(Did, MeasureCounter), Mutex<PeriodicCounter>>,
46 clock: Arc<dyn MeasureClock>,
47}
48
49trait MeasureClock: Send + Sync {
53 fn now(&self) -> DateTime<Utc>;
54}
55
56struct SystemMeasureClock;
57
58impl MeasureClock for SystemMeasureClock {
59 fn now(&self) -> DateTime<Utc> {
60 Utc::now()
61 }
62}
63
64#[derive(Debug)]
65struct PeriodicCounter {
66 period: Duration,
70 count: u64,
71 previous: DateTime<Utc>,
72 previous_count: u64,
73}
74
75impl PeriodicCounter {
76 fn new(period: u64, previous_count: u64, now: DateTime<Utc>) -> Self {
77 Self {
78 period: Duration::seconds(period as i64),
79 count: 0,
80 previous: now,
81 previous_count,
82 }
83 }
84
85 fn refresh_at(&mut self, now: DateTime<Utc>) -> bool {
87 if now - self.previous < self.period {
88 return false;
89 }
90
91 self.previous_count = self.count;
92 self.count = 0;
93 self.previous = now;
94 true
95 }
96
97 fn barely_get(&self) -> u64 {
99 if self.previous_count == 0 {
100 self.count
101 } else {
102 self.previous_count
103 }
104 }
105
106 fn incr_at(&mut self, now: DateTime<Utc>) -> (u64, bool) {
108 let is_refreshed = self.refresh_at(now);
109 self.count += 1;
110 (self.barely_get(), is_refreshed)
111 }
112
113 fn get_at(&mut self, now: DateTime<Utc>) -> (u64, bool) {
115 let is_refreshed = self.refresh_at(now);
116 (self.barely_get(), is_refreshed)
117 }
118}
119
120impl PeriodicMeasure {
121 pub fn new(storage: MeasureStorage) -> Self {
123 Self {
124 storage,
125 counters: DashMap::new(),
126 clock: Arc::new(SystemMeasureClock),
127 }
128 }
129
130 #[cfg(all(test, feature = "node"))]
131 fn new_with_clock(storage: MeasureStorage, clock: Arc<dyn MeasureClock>) -> Self {
132 Self {
133 storage,
134 counters: DashMap::new(),
135 clock,
136 }
137 }
138
139 fn gen_storage_key(did: Did, counter: MeasureCounter) -> String {
140 format!("PeriodicMeasure/counters/{did}/{counter:?}")
141 }
142
143 async fn ensure_counter(
145 &self,
146 did: Did,
147 counter: MeasureCounter,
148 now: DateTime<Utc>,
149 ) -> RefMut<'_, (Did, MeasureCounter), Mutex<PeriodicCounter>> {
150 let k = Self::gen_storage_key(did, counter);
151 let count = self
152 .storage
153 .get(&k)
154 .await
155 .unwrap_or_else(|e| {
156 log::error!("Failed to get counter: {e:?}");
157 Some(0)
158 })
159 .unwrap_or(0);
160 self.counters
161 .entry((did, counter))
162 .or_insert_with(|| Mutex::new(PeriodicCounter::new(DURATION, count, now)))
163 }
164
165 async fn save_counter(&self, did: Did, counter: MeasureCounter, count: u64) {
166 let k = Self::gen_storage_key(did, counter);
167 self.storage.put(&k, &count).await.unwrap_or_else(|e| {
168 log::error!("Failed to save counter: {e:?}");
169 })
170 }
171}
172
173#[cfg_attr(feature = "node", async_trait)]
174#[cfg_attr(feature = "browser", async_trait(?Send))]
175impl Measure for PeriodicMeasure {
176 async fn incr(&self, did: Did, counter: MeasureCounter) {
178 let now = self.clock.now();
179 let (count, is_refreshed) = {
180 let c = self.ensure_counter(did, counter, now).await;
181 let result = if let Ok(mut c) = c.lock() {
182 c.incr_at(now)
183 } else {
184 return;
185 };
186 result
187 };
188 if is_refreshed {
189 self.save_counter(did, counter, count).await;
190 }
191 }
192
193 async fn get_count(&self, did: Did, counter: MeasureCounter) -> u64 {
195 let now = self.clock.now();
196 let (count, is_refreshed) = {
197 let c = self.ensure_counter(did, counter, now).await;
198 let result = if let Ok(mut c) = c.lock() {
199 c.get_at(now)
200 } else {
201 return 0;
202 };
203 result
204 };
205 if is_refreshed {
206 self.save_counter(did, counter, count).await;
207 }
208 count
209 }
210}
211
212#[cfg_attr(feature = "node", async_trait)]
213#[cfg_attr(feature = "browser", async_trait(?Send))]
214impl measure::BehaviourJudgement for PeriodicMeasure {
215 async fn quality(&self, did: Did) -> PeerQuality {
216 let thresholds = PeerQualityThresholds::new(
217 crate::consts::CONNECT_FAILED_LIMIT,
218 crate::consts::MSG_SEND_FAILED_LIMIT,
219 crate::consts::MSG_RECV_FAILED_LIMIT,
220 );
221 PeerQualityEvidence::from_measure(self, did)
222 .await
223 .classify(thresholds)
224 }
225
226 async fn good(&self, did: Did) -> bool {
227 let connection_is_good = <Self as measure::ConnectBehaviour<
228 { crate::consts::CONNECT_FAILED_LIMIT },
229 >>::good(self, did)
230 .await;
231 let send_is_good = <Self as measure::MessageSendBehaviour<
232 { crate::consts::MSG_SEND_FAILED_LIMIT },
233 >>::good(self, did)
234 .await;
235 let receive_is_good = <Self as measure::MessageRecvBehaviour<
236 { crate::consts::MSG_RECV_FAILED_LIMIT },
237 >>::good(self, did)
238 .await;
239 connection_is_good && send_is_good && receive_is_good
240 }
241}
242
243#[cfg(test)]
244#[cfg(feature = "node")]
245mod tests {
246 use std::str::FromStr;
247 use std::sync::Arc;
248 use std::sync::Mutex;
249
250 use rings_core::measure::BehaviourJudgement;
251 use rings_core::storage::sled::SledStorage;
252 use rings_core::storage::MemStorage;
253
254 use super::*;
255
256 #[derive(Clone)]
257 struct ManualMeasureClock {
258 now: Arc<Mutex<DateTime<Utc>>>,
259 }
260
261 impl ManualMeasureClock {
262 fn new(now: DateTime<Utc>) -> Self {
263 Self {
264 now: Arc::new(Mutex::new(now)),
265 }
266 }
267
268 fn advance(&self, duration: Duration) {
269 let Ok(mut now) = self.now.lock() else {
270 panic!("manual measure clock lock poisoned");
271 };
272 let Some(advanced) = now.checked_add_signed(duration) else {
273 panic!("manual measure clock overflow");
274 };
275 *now = advanced;
276 }
277 }
278
279 impl MeasureClock for ManualMeasureClock {
280 fn now(&self) -> DateTime<Utc> {
281 let Ok(now) = self.now.lock() else {
282 panic!("manual measure clock lock poisoned");
283 };
284 now.to_owned()
285 }
286 }
287
288 fn advance_period(clock: &ManualMeasureClock) {
289 clock.advance(Duration::seconds(DURATION as i64));
290 }
291
292 #[tokio::test]
293 async fn test_measure_counter() {
294 let ms = Box::new(MemStorage::new());
295
296 let did1 = Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap();
297 let did2 = Did::from_str("0x999999cf1046e68e36E1aA2E0E07105eDDD1f08E").unwrap();
298
299 let clock = ManualMeasureClock::new(Utc::now());
300 let measure = PeriodicMeasure::new_with_clock(ms, Arc::new(clock.clone()));
301 assert_eq!(measure.get_count(did1, MeasureCounter::Sent).await, 0);
302 assert_eq!(measure.get_count(did2, MeasureCounter::Sent).await, 0);
303 assert_eq!(measure.get_count(did1, MeasureCounter::Received).await, 0);
304 assert_eq!(measure.get_count(did2, MeasureCounter::Received).await, 0);
305
306 measure.incr(did1, MeasureCounter::Sent).await;
307 measure.incr(did1, MeasureCounter::Received).await;
308
309 measure.incr(did2, MeasureCounter::Sent).await;
310 measure.incr(did2, MeasureCounter::Sent).await;
311 measure.incr(did2, MeasureCounter::Received).await;
312 measure.incr(did2, MeasureCounter::Received).await;
313 measure.incr(did2, MeasureCounter::Received).await;
314
315 assert_eq!(measure.get_count(did1, MeasureCounter::Sent).await, 1);
316 assert_eq!(measure.get_count(did2, MeasureCounter::Sent).await, 2);
317 assert_eq!(measure.get_count(did1, MeasureCounter::Received).await, 1);
318 assert_eq!(measure.get_count(did2, MeasureCounter::Received).await, 3);
319 }
320
321 #[tokio::test]
322 async fn test_measure_period() {
323 let ms = Box::new(MemStorage::new());
324
325 let did = Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap();
326
327 let clock = ManualMeasureClock::new(Utc::now());
328 let measure = PeriodicMeasure::new_with_clock(ms, Arc::new(clock.clone()));
329 assert_eq!(measure.get_count(did, MeasureCounter::Sent).await, 0);
330 assert_eq!(measure.get_count(did, MeasureCounter::Received).await, 0);
331
332 measure.incr(did, MeasureCounter::Sent).await;
333 measure.incr(did, MeasureCounter::Sent).await;
334 measure.incr(did, MeasureCounter::Received).await;
335
336 assert_eq!(measure.get_count(did, MeasureCounter::Sent).await, 2);
338 assert_eq!(measure.get_count(did, MeasureCounter::Received).await, 1);
339
340 advance_period(&clock);
341
342 measure.incr(did, MeasureCounter::Sent).await;
343 measure.incr(did, MeasureCounter::Received).await;
344 measure.incr(did, MeasureCounter::Received).await;
345 measure.incr(did, MeasureCounter::Received).await;
346
347 assert_eq!(measure.get_count(did, MeasureCounter::Sent).await, 2);
349 assert_eq!(measure.get_count(did, MeasureCounter::Received).await, 1);
350
351 advance_period(&clock);
352
353 assert_eq!(measure.get_count(did, MeasureCounter::Sent).await, 1);
355 assert_eq!(measure.get_count(did, MeasureCounter::Received).await, 3);
356
357 advance_period(&clock);
358
359 assert_eq!(measure.get_count(did, MeasureCounter::Sent).await, 0);
361 assert_eq!(measure.get_count(did, MeasureCounter::Received).await, 0);
362 }
363
364 #[tokio::test]
365 async fn test_persistent_measure_storage() {
366 let ms: MeasureStorage = Box::new(
367 SledStorage::new_with_cap_and_path(4096, "tmp/measure_test_db")
368 .await
369 .unwrap(),
370 );
371 ms.clear().await.unwrap();
372
373 let did = Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0").unwrap();
374 let clock = ManualMeasureClock::new(Utc::now());
375 let measure = PeriodicMeasure::new_with_clock(ms, Arc::new(clock.clone()));
376 assert_eq!(measure.get_count(did, MeasureCounter::Sent).await, 0);
377 assert_eq!(measure.get_count(did, MeasureCounter::Received).await, 0);
378
379 measure.incr(did, MeasureCounter::Sent).await;
380 measure.incr(did, MeasureCounter::Sent).await;
381 measure.incr(did, MeasureCounter::Received).await;
382
383 advance_period(&clock);
384
385 let c1 = measure.get_count(did, MeasureCounter::Sent).await;
387 assert_eq!(c1, 2);
388 let c2 = measure.get_count(did, MeasureCounter::Received).await;
389 assert_eq!(c2, 1);
390
391 drop(measure);
393
394 let ms2 = Box::new(
396 SledStorage::new_with_cap_and_path(4096, "tmp/measure_test_db")
397 .await
398 .unwrap(),
399 );
400 let measure2 = PeriodicMeasure::new_with_clock(ms2, Arc::new(clock));
401
402 assert_eq!(measure2.get_count(did, MeasureCounter::Sent).await, 2);
404 assert_eq!(measure2.get_count(did, MeasureCounter::Received).await, 1);
405 }
406
407 #[tokio::test]
408 async fn repeated_disconnections_degrade_peer_quality(
409 ) -> std::result::Result<(), Box<dyn std::error::Error>> {
410 let ms = Box::new(MemStorage::new());
411 let did = Did::from_str("0x11E807fcc88dD319270493fB2e822e388Fe36ab0")?;
412 let clock = ManualMeasureClock::new(Utc::now());
413 let measure = PeriodicMeasure::new_with_clock(ms, Arc::new(clock));
414
415 assert_eq!(measure.quality(did).await, PeerQuality::Unknown);
416
417 measure.incr(did, MeasureCounter::Connect).await;
418 assert_eq!(measure.quality(did).await, PeerQuality::Healthy);
419
420 for _ in 0..crate::consts::CONNECT_FAILED_LIMIT {
421 measure.incr(did, MeasureCounter::Disconnected).await;
422 }
423
424 assert_eq!(measure.quality(did).await, PeerQuality::Degraded);
425 Ok(())
426 }
427}