From 83b2f9efbbcedea3297fea6754ea4a0e7ac6b3e0 Mon Sep 17 00:00:00 2001 From: tv Date: Sun, 1 Nov 2026 20:59:18 +0100 Subject: libp2p: idle timeout follows keepalive --- src/simtest/identity.rs | 1 + src/transport/libp2p/behaviour.rs | 6 +++++- src/transport/libp2p/config.rs | 4 ++++ src/transport/libp2p/mod.rs | 10 +++++++++- src/transport/libp2p/tests.rs | 3 +++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/simtest/identity.rs b/src/simtest/identity.rs index f7ef521..7dbb722 100644 --- a/src/simtest/identity.rs +++ b/src/simtest/identity.rs @@ -228,6 +228,7 @@ async fn nodes_over_libp2p() { internet_hosts: Vec::new(), public: false, memory: true, + keepalive: super::KEEPALIVE, }) .unwrap(); config.transport = Arc::new(transport); diff --git a/src/transport/libp2p/behaviour.rs b/src/transport/libp2p/behaviour.rs index 18d8011..0b9be6b 100644 --- a/src/transport/libp2p/behaviour.rs +++ b/src/transport/libp2p/behaviour.rs @@ -188,6 +188,8 @@ pub(super) struct Features { pub(super) mdns: bool, pub(super) upnp: bool, pub(super) public: bool, + /// How long a connection may idle (`idle_timeout`). + pub(super) idle_timeout: Duration, } impl Features { @@ -197,6 +199,7 @@ impl Features { mdns: config.mdns, upnp: config.upnp, public: config.public, + idle_timeout: idle_timeout(config.keepalive), } } } @@ -228,6 +231,7 @@ impl Behaviour { mdns, upnp, public, + idle_timeout: _, } = features; let peer_id = key.public().to_peer_id(); let kad = public.then(|| { @@ -335,7 +339,7 @@ pub(super) fn swarm_config( features: Features, ) -> ::libp2p::swarm::Config { let config = config - .with_idle_connection_timeout(IDLE_TIMEOUT) + .with_idle_connection_timeout(features.idle_timeout) .with_dial_concurrency_factor(DIAL_CONCURRENCY); if features.public { config diff --git a/src/transport/libp2p/config.rs b/src/transport/libp2p/config.rs index 348a170..712f2e2 100644 --- a/src/transport/libp2p/config.rs +++ b/src/transport/libp2p/config.rs @@ -32,6 +32,9 @@ pub(crate) struct Libp2pConfig { pub(crate) public: bool, /// libp2p's in-memory transport instead of TCP and QUIC: for tests. pub(crate) memory: bool, + /// The node's keepalive interval (`--keepalive`): connections idle for a few of + /// them are closed (`idle_timeout`). + pub(crate) keepalive: Duration, } /// The multiaddrs of option `option`. @@ -138,6 +141,7 @@ impl Libp2pConfig { internet_hosts: settings.libp2p_internet_hosts.clone(), public: settings.libp2p_public, memory: false, + keepalive: settings.keepalive, }) } diff --git a/src/transport/libp2p/mod.rs b/src/transport/libp2p/mod.rs index a159280..3ebe7f6 100644 --- a/src/transport/libp2p/mod.rs +++ b/src/transport/libp2p/mod.rs @@ -128,8 +128,16 @@ const SLOW_SEND: Duration = Duration::from_millis(5); const MAX_MESSAGE: usize = 128 << 20; /// How long a connection may take to come up. const CONNECT_TIMEOUT: Duration = Duration::from_secs(10); -/// A connection nobody uses is closed after this; neighbours keep theirs busy. +/// A connection nobody uses is closed after this at least; neighbours keep theirs busy, +/// with a keepalive within one and a half intervals (`PeerState::take_keepalive_due`), +/// so a long interval lengthens it (`idle_timeout`). const IDLE_TIMEOUT: Duration = Duration::from_secs(300); + +/// How long a connection may idle before it is closed: `IDLE_TIMEOUT`, or three +/// keepalive intervals if that is longer. +fn idle_timeout(keepalive: Duration) -> Duration { + IDLE_TIMEOUT.max(keepalive * 3) +} /// Addresses of one dial tried at once (libp2p's default: 8). Hole punching (`HolePunching`) /// dials every address both sides offer, where they listen (LAN, VPNs, public IPv6) and /// where others saw them, often more than 8, while the other side punches towards all of diff --git a/src/transport/libp2p/tests.rs b/src/transport/libp2p/tests.rs index e105761..0f52e23 100644 --- a/src/transport/libp2p/tests.rs +++ b/src/transport/libp2p/tests.rs @@ -36,6 +36,7 @@ fn memory_node_with( internet_hosts: Vec::new(), public: false, memory: true, + keepalive: Duration::from_secs(60), }) .unwrap(); let address = if listen { @@ -185,6 +186,7 @@ async fn announced_addresses_are_published_instead() { internet_hosts: Vec::new(), public: false, memory: true, + keepalive: Duration::from_secs(60), }) .unwrap(); assert_eq!( @@ -426,6 +428,7 @@ async fn a_relay_that_is_no_node() { internet_hosts: Vec::new(), public: false, memory: true, + keepalive: Duration::from_secs(60), }) .unwrap(); let r_address = r_listen.with_p2p(r_peer).unwrap(); -- cgit v1.3.1