Skip to content

Commit 0a19473

Browse files
fix: don't apply reconnect backoff to discovered relay peers (#4029)
1 parent 77cb8a6 commit 0a19473

4 files changed

Lines changed: 78 additions & 14 deletions

File tree

logos_delivery/waku/node/peer_manager/peer_manager.nim

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,10 @@ proc loadFromStorage(pm: PeerManager) {.gcsafe.} =
223223
pm.switch.peerStore[ConnectionBook][peerId] = NotConnected
224224
# Reset connectedness state
225225
pm.switch.peerStore[DisconnectBook][peerId] = remotePeerInfo.disconnectTime
226-
pm.switch.peerStore[SourceBook][peerId] = remotePeerInfo.origin
226+
# Mark as Cache so it is distinguishable from live-discovered peers. If the
227+
# same peer is later rediscovered, addPeer overrides this with the live
228+
# origin, and reconnect backoff no longer applies to it.
229+
pm.switch.peerStore[SourceBook][peerId] = Cache
227230

228231
if remotePeerInfo.enr.isSome():
229232
pm.switch.peerStore[ENRBook][peerId] = remotePeerInfo.enr.get()
@@ -695,21 +698,28 @@ proc reconnectPeers*(
695698

696699
info "Reconnecting peers", proto = proto
697700

698-
# Proto is not persisted, we need to iterate over all peers.
699-
for peerInfo in pm.switch.peerStore.peers(protocolMatcher(proto)):
700-
# Check that the peer can be connected
701-
if peerInfo.connectedness == CannotConnect:
702-
error "Not reconnecting to unreachable or non-existing peer",
703-
peerId = peerInfo.peerId
704-
continue
701+
# Only reconnect peers that come from persistent storage (Cache). Freshly
702+
# discovered peers must not be delayed by the reconnect backoff: they are
703+
# connected right away by the relay connectivity loop. Rediscovered peers get
704+
# their origin overridden by addPeer, so they naturally drop out of this set.
705+
let peersToReconnect = pm.switch.peerStore.peers(protocolMatcher(proto)).filterIt(
706+
it.origin == Cache and it.connectedness != CannotConnect
707+
)
705708

706-
if backoffTime > ZeroDuration:
707-
info "Backing off before reconnect",
708-
peerId = peerInfo.peerId, backoffTime = backoffTime
709-
# We disconnected recently and still need to wait for a backoff period before connecting
710-
await sleepAsync(backoffTime)
709+
if peersToReconnect.len == 0:
710+
return
711711

712-
await pm.connectToNodes(@[peerInfo])
712+
# We disconnected recently and still need to wait a backoff period before
713+
# reconnecting. The wait is shared across all peers rather than applied once
714+
# per peer, so reconnection can't stall behind a slow/unreachable peer and
715+
# keep the node from taking on freshly discovered ones.
716+
if backoffTime > ZeroDuration:
717+
info "Backing off before reconnect", backoffTime = backoffTime
718+
await sleepAsync(backoffTime)
719+
720+
# Dial all reconnectable peers in parallel; a single slow dial must not delay
721+
# the rest.
722+
await allFutures(peersToReconnect.mapIt(pm.connectToNodes(@[it])))
713723

714724
proc getNumStreams*(pm: PeerManager, protocol: string): (int, int) =
715725
var

logos_delivery/waku/waku_core/peers.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type
4040
PeerExchange
4141
Dns
4242
Kademlia
43+
Cache # Loaded from persistent peer storage (not a live discovery source)
4344

4445
PeerDirection* = enum
4546
UnknownDirection

tests/node/test_wakunode_peer_manager.nim

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,11 @@ suite "Peer Manager":
614614
serverPeerStore.getPeer(clientPeerId).connectedness ==
615615
Connectedness.CanConnect
616616

617+
# reconnectPeers only acts on peers restored from persistent storage,
618+
# so tag the server as such. Peers coming from live discovery are
619+
# deliberately left out of the reconnect (and its backoff) path.
620+
client.peerManager.addPeer(serverRemotePeerInfo, origin = Cache)
621+
617622
# When triggering the reconnection
618623
await client.peerManager.reconnectPeers(WakuRelayCodec)
619624

tests/test_peer_manager.nim

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,54 @@ procSuite "Peer Manager":
300300

301301
await allFutures(nodes.mapIt(it.stop()))
302302

303+
asyncTest "reconnectPeers only reconnects persisted (Cache) peers":
304+
## Regression: freshly discovered relay peers were wrongly swept into the
305+
## reconnect backoff path. reconnectPeers must act only on peers loaded from
306+
## persistent storage (origin == Cache); discovered peers are left for the
307+
## connectivity loop to dial without any backoff.
308+
let node = newTestWakuNode(generateSecp256k1Key())
309+
await node.start()
310+
311+
let basePeerId = "QmeuZJbXrszW2jdT7GdduSjQskPU3S7vvGWKtKgDfkDvW"
312+
var cachePeerId, discoveredPeerId: PeerId
313+
require cachePeerId.init(basePeerId & "1")
314+
require discoveredPeerId.init(basePeerId & "2")
315+
316+
# Both peers advertise relay and point at an unreachable address, so a dial
317+
# attempt fails fast and is observable via the failed-connection counter.
318+
let unreachableAddr = MultiAddress.init("/ip4/127.0.0.1/tcp/1").tryGet()
319+
320+
node.peerManager.addPeer(
321+
RemotePeerInfo.init(
322+
peerId = cachePeerId, addrs = @[unreachableAddr], protocols = @[WakuRelayCodec]
323+
),
324+
origin = Cache,
325+
)
326+
node.peerManager.addPeer(
327+
RemotePeerInfo.init(
328+
peerId = discoveredPeerId,
329+
addrs = @[unreachableAddr],
330+
protocols = @[WakuRelayCodec],
331+
),
332+
origin = Discv5,
333+
)
334+
335+
# Default backoff is zero, so the test stays fast; the origin filter is what
336+
# we are validating here.
337+
await node.peerManager.reconnectPeers(WakuRelayCodec)
338+
339+
let peerStore = node.peerManager.switch.peerStore
340+
check:
341+
# The Cache peer was dialed (and failed against the unreachable address).
342+
peerStore[NumberFailedConnBook][cachePeerId] == 1
343+
peerStore.connectedness(cachePeerId) == CannotConnect
344+
345+
# The discovered peer was never touched by reconnectPeers.
346+
peerStore[NumberFailedConnBook][discoveredPeerId] == 0
347+
peerStore.connectedness(discoveredPeerId) == NotConnected
348+
349+
await node.stop()
350+
303351
asyncTest "Peer manager can use persistent storage and survive restarts":
304352
let
305353
database = SqliteDatabase.new(":memory:")[]

0 commit comments

Comments
 (0)