Skip to content

Fix peer selection so inbound connections cannot occupy all peer slots and prevent required outbound connections#10846

Merged
zilm13 merged 12 commits into
Consensys:masterfrom
zilm13:min-outgoing-fix
Jun 22, 2026
Merged

Fix peer selection so inbound connections cannot occupy all peer slots and prevent required outbound connections#10846
zilm13 merged 12 commits into
Consensys:masterfrom
zilm13:min-outgoing-fix

Conversation

@zilm13

@zilm13 zilm13 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

PR Description

  • Fix score-based peer connection calculation so reserved random outbound peers are not counted twice when determining remaining peer-count capacity (line 78)
  • Reserve capacity for required outbound peers using minRandomlySelectedPeers + subnetSubscribersRequired.
  • Count only locally initiated random peers as protected/random coverage.
  • When the remote-peer cap is exceeded, drop remotely initiated peers first before falling back to normal score-based drops.
  • Add tests for inbound saturation, remote random peers not being protected, subnet requirements, and remote-cap drop ordering.

Fixed Issue(s)

Documentation

  • I thought about documentation and added the doc-change-required label to this PR if updates are required.

Changelog

  • I thought about adding a changelog entry, and added one if I deemed necessary.

Note

Medium Risk
Changes live P2P connect/disconnect policy for the beacon node, which can affect peer counts and gossip reach; behavior is covered by new unit tests but impacts production networking.

Overview
Fixes Eth2 peer selection so inbound connections cannot fill the peer budget and block required outbound dials (minimum randomly selected peers plus attestation subnet subscribers).

Connect: Score-based capacity now subtracts randomlySelectedPeersToAdd (not total random pool size), and the random pool count used for targets includes only locally initiated random peers.

Disconnect: Computes a remote-peer cap from upperBound - (minRandomPeers + subnet requirement), drops remotely initiated peers first (by score), then fills remaining over-cap drops via the usual path (demote locally initiated random peers to score-based, then lowest scores). Inbound random peers are not protected like outbound random peers.

Config: Default minimum random peer count rises from 20% to 30% of the peer lower bound; minRandomlySelectedPeers is validated against maxPeers.

Reviewed by Cursor Bugbot for commit 41bb431. Bugbot is set up for automated code reviews on this repo. Configure here.

@zilm13 zilm13 marked this pull request as draft June 17, 2026 10:44
@zilm13 zilm13 marked this pull request as ready for review June 18, 2026 15:16
() ->
targetPeerCountRange.getMinimumRandomlySelectedPeerCount()
+ peerSubnetSubscriptions.getSubscribersRequired());
return peersBeingDropped.stream().toList();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return List.copyOf(peersBeingDropped); ?

@tbenr tbenr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some additional comments. Not sure i can approve yet :)

@zilm13

zilm13 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

@tbenr addressed

@tbenr tbenr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two more feedbacks:

--Xp2p-minimum-randomly-selected-peer-count should at least not allow inconsistent values compared to upperbound.
Additionally: do we want to allow the edgecase where all connections should be outbound (essentially aloways disconnecting inbounds?) if not we may want to enforce some reasonable values.. but i don't have a strong feeling here.

The remote-cap calculation currently drops remotely initiated peers even when total peer count is well below the lower bound. For example with upper=35, minRandom=25, and subnetRequired=1, the remote limit is 9, so a node with only 18 peers and 10 remotely initiated peers still disconnects. Could we make remote-cap drops conditional on being at/near the upper bound, or otherwise account for current headroom, so inbound peers are not dropped while the node is already below target?

@zilm13

zilm13 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author
  1. I have changed
  2. I don't agree. I've made explicitly more aggressive behavior to protect space for outgoing peers. So what's currently in PR:
  • outboundTartget = 18 / 100, inbound = 82 / 100
  • we start dropping inbound even if outbound is not reached, so when we have 8 outbound and 83 inbound, though it's under 100 total, we will drop 1 inbound
  • if we don't do this, in the same case we come to 100 with 92 inbound and 8 outbound, disconnect and connect blocks are separated, so how disconnect block should handle this? drop 10 on 100? drop 1? If it drops 1 in connect block it will have space for 1 peer to connect at most.
  • As blocks are separated and there are a lot of peers, peers could easily connect between disconnect and connect blocks.
  • For something in the middle I could suggest add protection to not drop until we are under lower bound. So if you set big number of outgoing this measure will jump in (but not on default settings, because 82 > 64)

@zilm13

zilm13 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

I've updated check along with my suggestion 8e63ff0
If you think we should handle this in a different way, we could discuss it

tbenr
tbenr previously approved these changes Jun 22, 2026

@tbenr tbenr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. just a simplification option

Comment on lines 61 to 70
public int getRemotelyInitiatedPeersToDrop(
final int currentRemotelyInitiatedPeerCount, final int totalOutboundRequirement) {
final int currentRemotelyInitiatedPeerCount,
final int totalOutboundRequirement,
final int currentTotalPeerCount) {
final int remotelyInitiatedPeerLimit = Math.max(0, upperBound - totalOutboundRequirement);
return Math.max(0, currentRemotelyInitiatedPeerCount - remotelyInitiatedPeerLimit);
final int remotelyInitiatedPeersAboveLimit =
Math.max(0, currentRemotelyInitiatedPeerCount - remotelyInitiatedPeerLimit);
final int peersAboveLowerBound = Math.max(0, currentTotalPeerCount - lowerBound);
return Math.min(remotelyInitiatedPeersAboveLimit, peersAboveLowerBound);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this blows a my mind a bit.

an alternative:

public int getRemotelyInitiatedPeersToDrop(
    final int currentRemotelyInitiatedPeerCount,
    final int totalOutboundRequirement,
    final int currentTotalPeerCount) {
  final int remotelyInitiatedPeerLimit = Math.max(0, upperBound - totalOutboundRequirement);
  final int maxPeersToDropWithoutGoingBelowLowerBound =
      Math.max(0, currentTotalPeerCount - lowerBound);

  return Math.clamp(
      currentRemotelyInitiatedPeerCount - remotelyInitiatedPeerLimit,
      0,
      maxPeersToDropWithoutGoingBelowLowerBound);
}

@tbenr tbenr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@zilm13 zilm13 enabled auto-merge (squash) June 22, 2026 13:20
@zilm13 zilm13 merged commit 64bee77 into Consensys:master Jun 22, 2026
154 of 157 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 22, 2026
@zilm13 zilm13 deleted the min-outgoing-fix branch June 22, 2026 15:04
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants