Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions waku/v2/peermanager/peer_selection.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"

"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"go.uber.org/zap"
Expand Down Expand Up @@ -91,6 +92,10 @@ func (pm *PeerManager) SelectRandom(criteria PeerSelectionCriteria) (peer.IDSlic
filteredPeers = pm.host.Peerstore().(wps.WakuPeerstore).PeersByPubSubTopics(criteria.PubsubTopics, filteredPeers...)
}

// Prefer peers we are already connected to, so we don't churn on dialing
// stale/unreachable peers learned via discovery or peer-exchange.
Comment on lines +95 to +96

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The func name is pretty self-explanatory

Suggested change
// Prefer peers we are already connected to, so we don't churn on dialing
// stale/unreachable peers learned via discovery or peer-exchange.

filteredPeers = pm.preferConnectedPeers(filteredPeers)

//Not passing excludePeers as filterPeers are already considering excluded ones.
randomPeers, err := selectRandomPeers(filteredPeers, nil, min(criteria.MaxPeers, len(peerIDs)))
if err != nil && len(peerIDs) == 0 {
Expand Down Expand Up @@ -121,6 +126,28 @@ func getRandom(filter PeerSet, count int, excludePeers PeerSet) (PeerSet, error)
return selectedPeers, nil
}

// preferConnectedPeers returns the subset of the given peers that we are
// currently connected to. If none are connected it returns the input unchanged.
//
// This biases peer selection toward reachable peers (e.g. an already-connected
// fleet/service node) instead of stale or unreachable peers learned via
// discv5/peer-exchange. Without this, selection is connectedness-blind and a
// light node can repeatedly pick dead peers for filter/lightpush, churning on
// failed dials and missing messages while a perfectly good connected peer is
// available (see status-im/status-go#7513).
func (pm *PeerManager) preferConnectedPeers(peers peer.IDSlice) peer.IDSlice {
connected := make(peer.IDSlice, 0, len(peers))
for _, p := range peers {
if pm.host.Network().Connectedness(p) == network.Connected {
connected = append(connected, p)
}
}
if len(connected) > 0 {
return connected
}
return peers
}

// selects count random peers from list of peers
func selectRandomPeers(peers peer.IDSlice, excludePeers PeerSet, count int) (PeerSet, error) {
filteredPeerMap := PeerSliceToMap(peers)
Expand Down Expand Up @@ -172,6 +199,8 @@ func (pm *PeerManager) selectServicePeer(criteria PeerSelectionCriteria) (PeerSe
}
slot.mu.RUnlock()
selectedPeers := pm.host.Peerstore().(wps.WakuPeerstore).PeersByPubSubTopics(criteria.PubsubTopics, keys...)
// Prefer already-connected service peers over stale/unreachable ones.
selectedPeers = pm.preferConnectedPeers(selectedPeers)
tmpPeers, err := selectRandomPeers(selectedPeers, criteria.ExcludePeers, criteria.MaxPeers)
for tmpPeer := range tmpPeers {
peers[tmpPeer] = struct{}{}
Expand Down
Loading