@@ -37,6 +37,9 @@ use crate::{
3737
3838mod guarded_channel;
3939
40+ #[ cfg( feature = "metrics" ) ]
41+ mod metrics;
42+
4043// TODO: use this
4144// /// Number of addresses that are not active that we keep around per endpoint.
4245// ///
@@ -75,6 +78,9 @@ const APPLICATION_ABANDON_PATH: u8 = 30;
7578/// in a high frequency, and to keep data about previous path around for subsequent connections.
7679const ACTOR_MAX_IDLE_TIMEOUT : Duration = Duration :: from_secs ( 60 ) ;
7780
81+ /// Interval in which connection and path metrics are emitted.
82+ const METRICS_INTERVAL : Duration = Duration :: from_secs ( 10 ) ;
83+
7884/// A stream of events from all paths for all connections.
7985///
8086/// The connection is identified using [`ConnId`]. The event `Err` variant happens when the
@@ -220,6 +226,7 @@ impl EndpointStateActor {
220226 trace ! ( "actor started" ) ;
221227 let idle_timeout = MaybeFuture :: None ;
222228 tokio:: pin!( idle_timeout) ;
229+ let mut metrics_interval = time:: interval ( METRICS_INTERVAL ) ;
223230 loop {
224231 let scheduled_path_open = match self . scheduled_open_path {
225232 Some ( when) => MaybeFuture :: Some ( time:: sleep_until ( when) ) ,
@@ -243,11 +250,7 @@ impl EndpointStateActor {
243250 self . handle_path_event( id, evt) ;
244251 }
245252 Some ( conn_id) = self . connections_close. next( ) , if !self . connections_close. is_empty( ) => {
246- self . connections. remove( & conn_id) ;
247- if self . connections. is_empty( ) {
248- trace!( "last connection closed - clearing selected_path" ) ;
249- self . selected_path. set( None ) . ok( ) ;
250- }
253+ self . handle_connection_close( conn_id) ;
251254 }
252255 _ = self . local_addrs. updated( ) => {
253256 trace!( "local addrs updated, triggering holepunching" ) ;
@@ -266,6 +269,9 @@ impl EndpointStateActor {
266269 self . scheduled_holepunch = None ;
267270 self . trigger_holepunching( ) . await ;
268271 }
272+ _ = metrics_interval. tick( ) => {
273+ self . record_metrics( ) ;
274+ }
269275 _ = & mut idle_timeout => {
270276 if self . connections. is_empty( ) && inbox. close_if_idle( ) {
271277 trace!( "idle timeout expired and still idle: terminate actor" ) ;
@@ -369,6 +375,7 @@ impl EndpointStateActor {
369375 ) {
370376 let pub_open_paths = Watchable :: default ( ) ;
371377 if let Some ( conn) = handle. upgrade ( ) {
378+ self . metrics . num_conns_opened . inc ( ) ;
372379 // Remove any conflicting stable_ids from the local state.
373380 let conn_id = ConnId ( conn. stable_id ( ) ) ;
374381 self . connections . remove ( & conn_id) ;
@@ -387,6 +394,8 @@ impl EndpointStateActor {
387394 paths : Default :: default ( ) ,
388395 open_paths : Default :: default ( ) ,
389396 path_ids : Default :: default ( ) ,
397+ #[ cfg( feature = "metrics" ) ]
398+ metrics : Default :: default ( ) ,
390399 } )
391400 . into_mut ( ) ;
392401
@@ -571,6 +580,19 @@ impl EndpointStateActor {
571580 tx. send ( rtt) . ok ( ) ;
572581 }
573582
583+ fn handle_connection_close ( & mut self , conn_id : ConnId ) {
584+ if let Some ( state) = self . connections . remove ( & conn_id) {
585+ #[ cfg( feature = "metrics" ) ]
586+ state. metrics . record_closed ( & self . metrics ) ;
587+ #[ cfg( not( feature = "metrics" ) ) ]
588+ let _ = state;
589+ }
590+ if self . connections . is_empty ( ) {
591+ trace ! ( "last connection closed - clearing selected_path" ) ;
592+ self . selected_path . set ( None ) . ok ( ) ;
593+ }
594+ }
595+
574596 /// Triggers holepunching to the remote endpoint.
575597 ///
576598 /// This will manage the entire process of holepunching with the remote endpoint.
@@ -1012,6 +1034,13 @@ impl EndpointStateActor {
10121034 }
10131035 }
10141036 }
1037+
1038+ fn record_metrics ( & mut self ) {
1039+ #[ cfg( feature = "metrics" ) ]
1040+ for state in self . connections . values_mut ( ) {
1041+ state. record_metrics_periodic ( & self . metrics , self . selected_path . get ( ) ) ;
1042+ }
1043+ }
10151044}
10161045
10171046/// Messages to send to the [`EndpointStateActor`].
@@ -1114,6 +1143,11 @@ struct ConnectionState {
11141143 open_paths : FxHashMap < PathId , transports:: Addr > ,
11151144 /// Reverse map of [`Self::paths].
11161145 path_ids : FxHashMap < transports:: Addr , PathId > ,
1146+ /// Tracker for stateful metrics for this connection and its paths
1147+ ///
1148+ /// Feature-gated on the `metrics` feature because it increases memory use.
1149+ #[ cfg( feature = "metrics" ) ]
1150+ metrics : self :: metrics:: MetricsTracker ,
11171151}
11181152
11191153impl ConnectionState {
@@ -1125,10 +1159,11 @@ impl ConnectionState {
11251159
11261160 /// Tracks an open path for the connection.
11271161 fn add_open_path ( & mut self , remote : transports:: Addr , path_id : PathId ) {
1162+ #[ cfg( feature = "metrics" ) ]
1163+ self . metrics . add_path ( path_id, & remote) ;
11281164 self . paths . insert ( path_id, remote. clone ( ) ) ;
11291165 self . open_paths . insert ( path_id, remote. clone ( ) ) ;
11301166 self . path_ids . insert ( remote, path_id) ;
1131-
11321167 self . update_pub_path_info ( ) ;
11331168 }
11341169
@@ -1138,11 +1173,15 @@ impl ConnectionState {
11381173 self . path_ids . remove ( & addr) ;
11391174 }
11401175 self . open_paths . remove ( path_id) ;
1176+ #[ cfg( feature = "metrics" ) ]
1177+ self . metrics . remove_path ( path_id) ;
11411178 }
11421179
11431180 /// Removes the path from the open paths.
11441181 fn remove_open_path ( & mut self , path_id : & PathId ) {
11451182 self . open_paths . remove ( path_id) ;
1183+ #[ cfg( feature = "metrics" ) ]
1184+ self . metrics . remove_path ( path_id) ;
11461185
11471186 self . update_pub_path_info ( ) ;
11481187 }
@@ -1162,6 +1201,19 @@ impl ConnectionState {
11621201
11631202 self . pub_open_paths . set ( new) . ok ( ) ;
11641203 }
1204+
1205+ #[ cfg( feature = "metrics" ) ]
1206+ fn record_metrics_periodic (
1207+ & mut self ,
1208+ metrics : & MagicsockMetrics ,
1209+ selected_path : Option < transports:: Addr > ,
1210+ ) {
1211+ let Some ( conn) = self . handle . upgrade ( ) else {
1212+ return ;
1213+ } ;
1214+ self . metrics
1215+ . record_periodic ( metrics, & conn, & self . open_paths , selected_path) ;
1216+ }
11651217}
11661218
11671219/// Watcher for the open paths and selected transmission path in a connection.
0 commit comments