Skip to content

Commit 62f0e65

Browse files
authored
fix: swarm panic while metric collection
Fixes #6153 Pull-Request: #6158.
1 parent aefbfbd commit 62f0e65

File tree

5 files changed

+109
-12
lines changed

5 files changed

+109
-12
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ libp2p-identity = { version = "0.2.12" }
8888
libp2p-kad = { version = "0.49.0", path = "protocols/kad" }
8989
libp2p-mdns = { version = "0.48.0", path = "protocols/mdns" }
9090
libp2p-memory-connection-limits = { version = "0.5.0", path = "misc/memory-connection-limits" }
91-
libp2p-metrics = { version = "0.17.0", path = "misc/metrics" }
91+
libp2p-metrics = { version = "0.17.1", path = "misc/metrics" }
9292
libp2p-mplex = { version = "0.43.1", path = "muxers/mplex" }
9393
libp2p-noise = { version = "0.46.1", path = "transports/noise" }
9494
libp2p-peer-store = { version = "0.1.0", path = "misc/peer-store" }

misc/metrics/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.17.1
2+
3+
- Fix panic in swarm metrics when `ConnectionClosed` events are received for connections that were established before metrics collection started.
4+
See [PR 6158](https://github.com/libp2p/rust-libp2p/pull/6158).
5+
16
## 0.17.0
27

38
- Update `prometheus-client` to `0.23.0`.

misc/metrics/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "libp2p-metrics"
33
edition.workspace = true
44
rust-version = { workspace = true }
55
description = "Metrics for libp2p"
6-
version = "0.17.0"
6+
version = "0.17.1"
77
authors = ["Max Inden <[email protected]>"]
88
license = "MIT"
99
repository = "https://github.com/libp2p/rust-libp2p"

misc/metrics/src/swarm.rs

Lines changed: 101 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -228,15 +228,20 @@ impl<TBvEv> super::Recorder<SwarmEvent<TBvEv>> for Metrics {
228228
},
229229
cause: cause.as_ref().map(Into::into),
230230
};
231-
self.connections_duration.get_or_create(&labels).observe(
232-
self.connections
233-
.lock()
234-
.expect("lock not to be poisoned")
235-
.remove(connection_id)
236-
.expect("closed connection to previously be established")
237-
.elapsed()
238-
.as_secs_f64(),
239-
);
231+
232+
// Only record connection duration if we have a record of when it was established.
233+
// This gracefully handles cases where ConnectionClosed events are received
234+
// for connections that were established before metrics collection started.
235+
if let Some(established_time) = self
236+
.connections
237+
.lock()
238+
.expect("lock not to be poisoned")
239+
.remove(connection_id)
240+
{
241+
self.connections_duration
242+
.get_or_create(&labels)
243+
.observe(established_time.elapsed().as_secs_f64());
244+
}
240245
}
241246
SwarmEvent::IncomingConnection { send_back_addr, .. } => {
242247
self.connections_incoming
@@ -453,3 +458,90 @@ impl From<&libp2p_swarm::ListenError> for IncomingConnectionError {
453458
}
454459
}
455460
}
461+
462+
#[cfg(test)]
463+
mod tests {
464+
use std::time::Duration;
465+
466+
use libp2p_core::ConnectedPoint;
467+
use libp2p_swarm::{ConnectionId, SwarmEvent};
468+
use prometheus_client::registry::Registry;
469+
470+
use super::*;
471+
use crate::Recorder;
472+
473+
#[test]
474+
fn test_connection_closed_without_established() {
475+
let mut registry = Registry::default();
476+
let metrics = Metrics::new(&mut registry);
477+
478+
// Create a fake ConnectionClosed event for a connection that was never tracked.
479+
let connection_id = ConnectionId::new_unchecked(1);
480+
let endpoint = ConnectedPoint::Dialer {
481+
address: "/ip4/127.0.0.1/tcp/8080".parse().unwrap(),
482+
role_override: libp2p_core::Endpoint::Dialer,
483+
port_use: libp2p_core::transport::PortUse::New,
484+
};
485+
486+
let event = SwarmEvent::<()>::ConnectionClosed {
487+
peer_id: libp2p_identity::PeerId::random(),
488+
connection_id,
489+
endpoint,
490+
num_established: 0,
491+
cause: None,
492+
};
493+
494+
// This should NOT panic.
495+
metrics.record(&event);
496+
497+
// Verify that the connections map is still empty (no connection was removed).
498+
let connections = metrics.connections.lock().expect("lock not to be poisoned");
499+
assert!(connections.is_empty());
500+
}
501+
502+
#[test]
503+
fn test_connection_established_then_closed() {
504+
let mut registry = Registry::default();
505+
let metrics = Metrics::new(&mut registry);
506+
507+
let connection_id = ConnectionId::new_unchecked(1);
508+
let endpoint = ConnectedPoint::Dialer {
509+
address: "/ip4/127.0.0.1/tcp/8080".parse().unwrap(),
510+
role_override: libp2p_core::Endpoint::Dialer,
511+
port_use: libp2p_core::transport::PortUse::New,
512+
};
513+
514+
// First, establish a connection.
515+
let established_event = SwarmEvent::<()>::ConnectionEstablished {
516+
peer_id: libp2p_identity::PeerId::random(),
517+
connection_id,
518+
endpoint: endpoint.clone(),
519+
num_established: std::num::NonZeroU32::new(1).unwrap(),
520+
concurrent_dial_errors: None,
521+
established_in: Duration::from_millis(100),
522+
};
523+
524+
metrics.record(&established_event);
525+
526+
// Verify connection was added.
527+
{
528+
let connections = metrics.connections.lock().expect("lock not to be poisoned");
529+
assert!(connections.contains_key(&connection_id));
530+
}
531+
532+
// Now close the connection.
533+
let closed_event = SwarmEvent::<()>::ConnectionClosed {
534+
peer_id: libp2p_identity::PeerId::random(),
535+
connection_id,
536+
endpoint,
537+
num_established: 0,
538+
cause: None,
539+
};
540+
541+
metrics.record(&closed_event);
542+
543+
// Verify connection was removed.
544+
let connections = metrics.connections.lock().expect("lock not to be poisoned");
545+
assert!(!connections.contains_key(&connection_id));
546+
}
547+
}

0 commit comments

Comments
 (0)