feat(kad): re-seed a routing table that fell below a minimum size - #2937
feat(kad): re-seed a routing table that fell below a minimum size#2937gmelodie wants to merge 4 commits into
Conversation
e311e51 to
5d888fa
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2937 +/- ##
==========================================
+ Coverage 82.25% 82.30% +0.04%
==========================================
Files 178 178
Lines 32466 32511 +45
Branches 12 11 -1
==========================================
+ Hits 26704 26757 +53
+ Misses 5762 5754 -8
🚀 New features to boost your workflow:
|
|
|
||
| await kad.refreshTable(kad.rtable, forceRefresh = true) | ||
|
|
||
| proc maintainMinPeers(kad: KadDHT) {.async: (raises: [CancelledError]).} = |
There was a problem hiding this comment.
it's unnecessary to raise here
There was a problem hiding this comment.
You mean the {.async: (raises: [CancelledError]).} part? Or the doAssert below it? I removed the doAssert
There was a problem hiding this comment.
Pull request overview
This PR improves Kademlia DHT self-healing by introducing a periodic “fix low peers” mechanism that re-seeds and refreshes the routing table when it drops below a configured minimum, using connected peers and configured bootstrap nodes.
Changes:
- Added
fixLowPeersand a backgroundmaintainMinPeersloop, started/stopped with the KadDHT lifecycle. - Extended
KadDHTConfigwithfixLowPeersIntervalandminRoutingTableSize, plus a newkad_routing_table_reseedsmetric. - Refactored bootstrap seeding to flow through
initKadBase, and centralized tuple→PeerInfoconversion viatoPeerInfos.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/libp2p/kademlia/utils.nim | Extends test config helper to include new fix-low-peers config parameters. |
| tests/libp2p/kademlia/test_fix_low_peers.nim | Adds unit tests covering reseed/refresh behavior and the background loop. |
| libp2p/protocols/service_discovery.nim | Routes bootstrap nodes through initKadBase for consistent seeding. |
| libp2p/protocols/kademlia/types.nim | Adds new config defaults/fields and a new KadDHT loop field + stored bootstrap nodes. |
| libp2p/protocols/kademlia/routing_table.nim | Introduces peerCount() and reuses it in routing table metrics. |
| libp2p/protocols/kademlia/kademlia_metrics.nim | Adds a counter for routing table reseed attempts. |
| libp2p/protocols/kademlia/find.nim | Adds toPeerInfos helper and updates updatePeers overload to use it. |
| libp2p/protocols/kademlia.nim | Implements connectedPeerInfos, fixLowPeers, and the periodic reseed loop; wires loop into start/stop. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Summary
Bootstrap runs one time, in
KadDHT.start. After that only the 10-minute bucket refresh (maintainBuckets) runs, and it can only refresh peers that the table already holds. A node that loses its peers to an eviction storm, or that comes back from a network partition, has no path back into the DHT: an empty or near-empty table gives the refresh nothing to walk from.This PR adds
fixLowPeers, the equivalent of the go-libp2prunFixLowPeersLoop. A background loop compares the routing table againstconfig.minRoutingTableSizeeveryconfig.fixLowPeersInterval. When the table is short, the loop re-seeds it from two sources and then forces a refresh:admitPeers), because a live connection does not prove that the peer speaks the DHT protocol.updatePeers), because the operator already trusted them at construction time, and because the admission probes only land after the current pass.The forced refresh (
refreshTable(forceRefresh = true)) then walks every bucket, and not only the stale ones, so the re-seeded peers give closer peers immediately.To make the bootstrap nodes available after construction,
KadDHTnow keeps them in abootstrapNodesfield.initKadBasetakes them and does the initialupdatePeers, soKadDHT.newandServiceDiscovery.newshare one seeding path instead of two copies.Affected Areas
Kademlia only.
libp2p/protocols/kademlia.nimgetsconnectedPeerInfos,fixLowPeersand themaintainMinPeersloop, started instartand cancelled instop.routing_table.nimgetspeerCount, whichupdateRoutingTableMetricsnow reuses.find.nimgetstoPeerInfos, so the tuple toPeerInfoconversion lives in one place.service_discovery.nimpasses its bootstrap nodes throughinitKadBase.Compatibility & Downstream Validation
No API break, so no downstream branch is needed.
Impact on Library Users
No API change.
KadDHTConfig.newgets two new optional parameters,fixLowPeersInterval(default 1 minute) andminRoutingTableSize(default 10 peers). Existing call sites keep compiling and get the loop with the defaults.Behaviour change: a node with fewer than 10 peers in its routing table now re-dials its bootstrap nodes and its connected peers one time per minute until it recovers. Set
minRoutingTableSize = 0to turn the re-seed off.New metric:
kad_routing_table_reseeds, a counter of the re-seed attempts.Risk Assessment
peerCount() >= minRoutingTableSize.switch.connectedPeers(), so an inbound connection can offer itself. It still has to answer a FIND_NODE probe and pass the per-IP and per-subnet caps inadmissibleAddrs, and the probes run behindadmissionSem.Tests
tests/libp2p/kademlia/test_fix_low_peers.nimcovers five cases: a healthy table triggers no lookup, a short table forces a refresh of every non-empty bucket, an evicted bootstrap node returns to the table, a connected peer is admitted into an empty table, and the loop itself re-seeds with no explicit call.References
Closes #2859