Repository navigation
fix(p2p): disconnect peers whose gossipsub send queue never drains - #739
MegaRedHand wants to merge 3 commits into
Conversation
A gossipsub connection handler disables itself for good after a few substream negotiations on one connection, successful ones included, and then never takes another message off its send queue. The behaviour is not told, so it keeps queueing forwards for that peer until the queue is full, and a queued message is only dropped when it is taken off. The copies stay in memory until the connection closes, which other protocols can keep open for hours: on the mainnet follower these queues held about 490 MB. Gossipsub reports such a peer as SlowPeer every heartbeat, and the node ignored the event. The swarm task now collects those reports and, on the 10 s check that already drops graylisted peers, disconnects a peer that was reported in each of the last six checks. The follower's three stuck peers failed without a break for 5 to 63 minutes, so each would have been dropped within about a minute, while a peer with a few isolated failures keeps its connection. Beacon only, gated like the graylist check on peer scoring being on.
🤖 Claude Code ReviewReview: PR 739, disconnect peers whose gossipsub send queue never drainsThe change is small, self-contained and well reasoned. I found no blocking issues. I read the diff only and did not build it or run the tests. Correctness
Points worth a look (non-blocking)
Tests and docs
Looks good to merge. Point 1 is the only change I'd suggest making. Automated review by Claude (Anthropic) · sonnet · custom prompt |
🤖 Codex Code ReviewLooks good overall. The change is narrowly scoped to P2P hygiene, not consensus logic, and the approach is reasonable for mitigating the documented gossipsub queue retention issue.
One thing I’d flag:
Nice-to-have:
If you want, I can turn the first point into a suggested patch. Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Kimi Code ReviewI'll review this PR which adds slow peer disconnection for gossipsub peers with dead streams that accumulate queued messages in memory. Overall AssessmentThis is a well-structured, targeted fix for a memory leak caused by gossipsub connection handlers that disable themselves but leave queues pinned in memory. The logic is sound, tests are comprehensive, and documentation is thorough. I have a few minor concerns. Issues Found1. Race condition in
|
| Item | Severity | File:Lines | Description |
|---|---|---|---|
| 1 | Low | swarm_adapter.rs:668-677 |
Test a_returned_peer_starts_a_new_streak doesn't verify peer was actually returned during loop |
| 2 | Info | swarm_adapter.rs:402-408 |
peer_score(local_peer_id) mode check is correct but implicit; consider explicit mode enum |
| 3 | Info | swarm_adapter.rs:339-358 |
Doc comment mentions "490 MB" — consider if this is mainnet-specific and may need updating |
Recommendation: Fix test in Item 1 to assert on loop behavior. Otherwise, this PR is ready to merge.
Automated review by Kimi (Moonshot AI) · kimi-k2.6 · custom prompt
) A gossipsub connection handler disables itself for good after a few substream negotiations on one connection, successful ones included, and then never takes another message off its send queue. The behaviour is not told, so it keeps queueing forwards for that peer until the queue is full, and a queued message is only dropped when it is taken off. The copies stay in memory until the connection closes, which other protocols can keep open for hours: on the mainnet follower these queues held about 490 MB. Gossipsub reports such a peer as SlowPeer every heartbeat, and the node ignored the event. The swarm task now collects those reports and, on the 10 s check that already drops graylisted peers, disconnects a peer that was reported in each of the last six checks. The follower's three stuck peers failed without a break for 5 to 63 minutes, so each would have been dropped within about a minute, while a peer with a few isolated failures keeps its connection. Beacon only, gated like the graylist check on peer scoring being on. Cherry-picked from fix/p2p-disconnect-slow-gossipsub-peers at 380fd29.
|
One availability risk worth weighing before merging. In the pinned fork, a forward that sits in the handler's queue longer than If the swarm task is starved for a while, or the uplink is saturated, every
Two cheap ways to close it:
|
…hem per check Gossipsub counts a forward that expired in a peer's send queue as the same non-priority failure as a full queue. A saturated uplink or a stalled runtime on our side therefore makes every peer look slow at once, and the check would disconnect the whole peer set in one tick. - A SlowPeer report now counts only when its priority or non-priority failures exceed SLOW_PEER_REPORT_THRESHOLD, the cut Lighthouse applies before penalizing. A dead stream fails every message queued for the peer; a few failures per heartbeat are more likely late forwards. - One check disconnects at most MAX_SLOW_PEER_DISCONNECTS_PER_CHECK peers, the most failed first. The rest keep their streak and are logged as a warning, since many peers failing together points at our side.
|
@pablodeymo done |
🗒️ Description / Motivation
A heap profile of our mainnet follower (2.8 GB steady RSS) showed about 490 MB of gossipsub message copies that never went away: they sat in the send queues of a few peers whose gossipsub connection handler had disabled itself.
The mechanism, in the pinned rust-libp2p (same in upstream master):
MAX_SUBSTREAM_ATTEMPTSsets itself toDisabled(MaxSubstreamAttempts). In that statepollisPendingforever, so the send queue is never read again.Gossipsub does report such a peer:
Event::SlowPeerevery heartbeat. We dropped that event. Gossipsub's own score penalty for it (slow_peer_weight) left the peer in thenegativeband, nowhere near the graylist. Lighthouse handles the event in its peer manager instead.What Changed
crates/net/p2p/src/swarm_adapter.rs:SlowPeerscollectsSlowPeerreports between the existing 10 s checks. A report counts only when its priority or its non-priority failures exceedSLOW_PEER_REPORT_THRESHOLD(10), the cut Lighthouse applies before it penalizes a slow peer.disconnect_slow_peersruns on that tick, right afterdisconnect_graylisted_peers. It disconnects a peer reported in each of the lastSLOW_PEER_CHECK_LIMIT(6) checks, so about a minute without a break.MAX_SLOW_PEER_DISCONNECTS_PER_CHECK(10) peers, the ones with the most failed messages first. The others keep their streak, so a peer still failing at the next check is disconnected then. Peers held back by the cap are logged as a warning.crates/net/p2p/src/metrics.rs: adds thelean_gossipsub_slow_peer_disconnects_totalcounter.docs/beacon_wire.md,docs/metrics.md,CLAUDE.md: describe the rule, its two limits and the metric.Correctness / Behavior Guarantees
connection_handler_forward_duration) as the samenon_priorityfailure as a full queue, so a saturated uplink or a stalled runtime on our side makes every peer look slow at once. The threshold filters short stalls, and the cap bounds how many peers one check can drop. A sustained slowdown can still cost up toMAX_SLOW_PEER_DISCONNECTS_PER_CHECKpeers per check while discovery refills them.SLOW_PEER_REPORT_THRESHOLDand keep its connection.libp2p_gossipsublogs at ERROR, gossipsub's "Send Queue full" WARNs no longer show by default. An operator now sees these peers through this change'sinfo!disconnect line and the new counter.Tests Added / Run
SlowPeers:SLOW_PEER_REPORT_THRESHOLDfailures within a single 700 ms heartbeat, so the replay does not yet confirm the threshold still catches them.cargo test --profile release-fast -p ethlambda-p2p --lib: 370 passed, 1 ignored.Related Issues / PRs
✅ Verification Checklist
make fmt— cleanmake lint(clippy with-D warnings) — clean. Rancargo clippy --locked -p ethlambda-p2p --all-targets -- -D warnings(clean), not the full workspace targetmake test(test-consensusplustest-node, atrelease-fast) — all passing. Ran only the p2p lib tests above