eth: use hysteresis to stabilize peer selection for transaction broadcast

When maxPeers was just above some perfect square, and a few peers
dropped for some reason, we changed the peer selection function.
When new peers were acquired, we changed again.

This patch stabilizes the selection function under normal operating
conditions by adding some hysteresis.

Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com>
This commit is contained in:
Csaba Kiraly 2025-04-25 12:32:55 +02:00 committed by Felix Lange
parent c022c3f8ab
commit 44e3cd5582

View file

@ -57,6 +57,9 @@ const (
// All transactions with a higher size will be announced and need to be fetched // All transactions with a higher size will be announced and need to be fetched
// by the peer. // by the peer.
txMaxBroadcastSize = 4096 txMaxBroadcastSize = 4096
// Hysteresis to stabilize the number of direct peers to send transactions to.
directPeersHysteresis = 0.5
) )
var syncChallengeTimeout = 15 * time.Second // Time allowance for a node to reply to the sync progress challenge var syncChallengeTimeout = 15 * time.Second // Time allowance for a node to reply to the sync progress challenge
@ -118,6 +121,7 @@ type handler struct {
txpool txPool txpool txPool
chain *core.BlockChain chain *core.BlockChain
maxPeers int maxPeers int
lastDirect int64 // Last number of peers we sent transactions to, used to stabilize the randomness
downloader *downloader.Downloader downloader *downloader.Downloader
txFetcher *fetcher.TxFetcher txFetcher *fetcher.TxFetcher
@ -482,13 +486,20 @@ func (h *handler) BroadcastTransactions(txs types.Transactions) {
annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce
) )
// Broadcast transactions to a batch of peers not knowing about it // Broadcast transactions to a batch of peers not knowing about it
sqrtPeers := int64(math.Sqrt(float64(h.peers.len()))) // Approximate number of peers to broadcast to sqrtPeers := math.Sqrt(float64(h.peers.len())) // Approximate number of peers to broadcast to
// If maxPeers is just above some perfect square, we need to stabilize // Use some hysteresis to avoid oscillating between two values, stabilising the modulus in the peer selection
// the number to avoid frequent changes when a few peers drop. // If the number of peers is small, use a minimum of 1 peer
maxDirect := int64(math.Sqrt(float64(h.maxPeers)) - 0.5) var directInt int64
direct := big.NewInt(max(min(sqrtPeers, maxDirect), 1)) lastDirect := atomic.LoadInt64(&h.lastDirect)
if int64(sqrtPeers) >= lastDirect {
directInt = max(int64(sqrtPeers), 1)
} else {
directInt = max(min(int64(sqrtPeers+directPeersHysteresis), lastDirect), 1)
}
atomic.StoreInt64(&h.lastDirect, directInt)
direct := big.NewInt(directInt) // Number of peers to send directly to
total := new(big.Int).Exp(direct, big.NewInt(2), nil) // Stabilise total peer count a bit based on sqrt peers total := new(big.Int).Exp(direct, big.NewInt(2), nil) // Stabilise total peer count a bit based on sqrt peers
var ( var (