From 3a355b1617fe14e0b303be8f20f365db12398d16 Mon Sep 17 00:00:00 2001 From: Raj Singh Date: Sun, 15 Feb 2026 14:07:12 -0600 Subject: [PATCH] fix: add TCP connect timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit known_addrs in PeerInfoInternal is append-only — addresses accumulate via add_addr() and PeerList gossip but are never removed. In dynamic environments (k8s pod restarts, DHCP, NAT traversal), this list grows unboundedly with stale addresses. Combined with sequential iteration in try_connect() and no TCP connect timeout in netapp.rs, each unreachable address blocks reconnection for the kernel's TCP SYN timeout (75-130s on Linux). With 10+ stale addresses, worst-case reconnection exceeds 750s — a full outage for replication_factor=3 clusters. This patches includes a first change to fix this issue: 1. TCP connect timeout (netapp.rs): Wrap TcpStream::connect() in tokio::time::timeout(10s). Caps per-address attempt from 75-130s to 10s, reducing worst-case 10-addr reconnection from ~750s to ~100s. --- src/net/netapp.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/net/netapp.rs b/src/net/netapp.rs index da6eb057..4a53f1d1 100644 --- a/src/net/netapp.rs +++ b/src/net/netapp.rs @@ -45,6 +45,11 @@ const TCP_KEEPALIVE_TIME: Duration = Duration::from_secs(30); /// Interval between keepalive probes after the first. const TCP_KEEPALIVE_INTERVAL: Duration = Duration::from_secs(10); +/// Timeout for outgoing TCP connection attempts. +/// Caps per-address connection time instead of relying on the kernel's +/// TCP SYN timeout (75-130s on Linux, ~20s on macOS). +const CONNECT_TIMEOUT: Duration = Duration::from_secs(10); + fn set_keepalive(stream: &TcpStream) -> Result<(), std::io::Error> { let sock_ref = socket2::SockRef::from(stream); let keepalive = socket2::TcpKeepalive::new() @@ -331,9 +336,13 @@ impl NetApp { TcpSocket::new_v6()? }; socket.bind(SocketAddr::new(addr, 0))?; - socket.connect(ip).await? + tokio::time::timeout(CONNECT_TIMEOUT, socket.connect(ip)) + .await + .map_err(|_| Error::Message(format!("connect to {} timed out", ip)))?? } - None => TcpStream::connect(ip).await?, + None => tokio::time::timeout(CONNECT_TIMEOUT, TcpStream::connect(ip)) + .await + .map_err(|_| Error::Message(format!("connect to {} timed out", ip)))??, }; if let Err(e) = set_keepalive(&stream) { warn!("Failed to set keepalive on connection to {}: {}", ip, e);