@@ -285,6 +285,9 @@ impl PathValidity {
285285
286286 let quality = state. congestion_metrics . quality_score ( ) ;
287287 metrics. path_quality_score . observe ( quality) ;
288+
289+ let latency_ms = state. latency . as_secs_f64 ( ) * 1000.0 ;
290+ metrics. connection_latency_ms . observe ( latency_ms) ;
288291 }
289292}
290293
@@ -395,4 +398,36 @@ mod tests {
395398 let quality = validity. quality_score ( ) ;
396399 assert ! ( quality < 0.9 ) ; // Should be penalized
397400 }
401+
402+ #[ tokio:: test]
403+ async fn test_connection_latency_histogram ( ) {
404+ use crate :: magicsock:: Metrics as MagicsockMetrics ;
405+
406+ let metrics = MagicsockMetrics :: default ( ) ;
407+ let mut validity = PathValidity :: new ( Instant :: now ( ) , Duration :: from_millis ( 10 ) ) ;
408+
409+ validity. record_metrics ( & metrics) ;
410+ assert_eq ! ( metrics. connection_latency_ms. count( ) , 1 ) ;
411+
412+ validity. update_pong ( Instant :: now ( ) , Duration :: from_millis ( 25 ) ) ;
413+ validity. record_metrics ( & metrics) ;
414+ assert_eq ! ( metrics. connection_latency_ms. count( ) , 2 ) ;
415+
416+ validity. update_pong ( Instant :: now ( ) , Duration :: from_millis ( 50 ) ) ;
417+ validity. record_metrics ( & metrics) ;
418+ assert_eq ! ( metrics. connection_latency_ms. count( ) , 3 ) ;
419+
420+ validity. update_pong ( Instant :: now ( ) , Duration :: from_millis ( 100 ) ) ;
421+ validity. record_metrics ( & metrics) ;
422+ assert_eq ! ( metrics. connection_latency_ms. count( ) , 4 ) ;
423+
424+ let buckets = metrics. connection_latency_ms . buckets ( ) ;
425+ assert ! ( !buckets. is_empty( ) ) ;
426+
427+ let p50 = metrics. connection_latency_ms . percentile ( 0.5 ) ;
428+ assert ! ( p50 > 10.0 && p50 < 100.0 ) ;
429+
430+ let p95 = metrics. connection_latency_ms . percentile ( 0.95 ) ;
431+ assert ! ( p95 >= p50) ;
432+ }
398433}
0 commit comments