Skip to content

fix(network): stop zero-connection recovery re-dialing orphaned peers - #100

Draft
lukyrys wants to merge 107 commits into
mainfrom
fix/zero-recovery-orphan-dials
Draft

fix(network): stop zero-connection recovery re-dialing orphaned peers#100
lukyrys wants to merge 107 commits into
mainfrom
fix/zero-recovery-orphan-dials

Conversation

@lukyrys

@lukyrys lukyrys commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Stops a node with no connections from re-dialing peers that are long gone.

Zero-connection recovery walks a list of bootstrap addresses every 30 seconds and dials each one with a 10 second timeout. That list was append-only: an address learned from gossip went in before any dial proved the peer existed, and nothing ever took it out again. Once libp2p's peerStore aged the peer out after two hours, re-dial maintenance — which walks the peerStore — could not see it either, so the address stayed on the recovery list for good and every tick spent its budget on dead hosts.

  • bootstrap addresses now carry where they came from and when, and gossip-discovered ones expire after two hours or once 200 of them pile up
  • an address whose peer is connected right now never expires, so a healthy long-lived peer keeps the keep-alive tag that drives reconnects
  • recovery honours the same re-dial backoff the maintenance loop uses, so a dead discovered address is paced instead of retried every single tick
  • configured bootstrap addresses are dialled first and are never paced or expired — they are the user's own way back into the network
  • recovery re-reads the live connection state instead of trusting the snapshot the status tick took before maintenance ran

Follow-up needed once #72 is merged, written out here so it does not have to be worked out again.

#72 introduces an unreachableQuarantine map (peer ID to the epoch ms it was evicted at, held for UNREACHABLE_QUARANTINE_MS). That map does not exist on main, so this PR cannot consult it — recovery currently paces a dead discovered peer through the backoff only. After #72 lands, recovery should skip a quarantined peer outright:

  • give isRecoveryDialEligible in backend/src/protocol/network.ts two more parameters, the quarantine map and its window, and return false for a discovered entry whose peer ID has a quarantine stamp newer than the window; configured entries stay exempt exactly as they are for the backoff
  • pass this.unreachableQuarantine at the single call site in runZeroConnectionRecovery
  • this PR renames the bootstrapMultiaddrs array to bootstrapEntries (now objects, not bare multiaddrs), so the two places fix(network): expire stale and unreachable peers from the network participant list #72 rebuilds that array need adapting: purgeStalePeer becomes this.bootstrapEntries = this.bootstrapEntries.filter(e => e.peerID !== peerID), and the identity-mismatch filter in addBootstrapPeers matches on e.peerID and e.ma.toString() instead of the raw multiaddr
  • add two cases to backend/tests/unit/protocol/bootstrap-recovery.test.ts: a quarantined discovered entry is skipped by recovery, and a quarantined configured entry is still dialled

Expect a merge conflict in runZeroConnectionRecovery and addBootstrapPeers whichever of the two merges second — both touch the same lines, and the resolution is the rename above.

lukyrys added 30 commits July 23, 2026 08:23
…ale-discovered-peers

# Conflicts:
#	backend/src/protocol/network.ts
lukyrys added 30 commits August 17, 2026 04:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant