diff --git a/eth/handler.go b/eth/handler.go index 8283d7d02fe2..86e94cada6d1 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -52,6 +52,9 @@ const ( // All transactions with a higher size will be announced and need to be fetched // by the peer. 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 @@ -110,10 +113,11 @@ type handler struct { snapSync atomic.Bool // Flag whether snap sync is enabled (gets disabled if we already have blocks) synced atomic.Bool // Flag whether we're considered synchronised (enables transaction processing) - database ethdb.Database - txpool txPool - chain *core.BlockChain - maxPeers int + database ethdb.Database + txpool txPool + chain *core.BlockChain + maxPeers int + lastDirect atomic.Int64 // Last number of peers we sent transactions to, used to stabilize the randomness downloader *downloader.Downloader txFetcher *fetcher.TxFetcher @@ -477,11 +481,21 @@ func (h *handler) BroadcastTransactions(txs types.Transactions) { annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce ) // Broadcast transactions to a batch of peers not knowing about it - direct := big.NewInt(int64(math.Sqrt(float64(h.peers.len())))) // Approximate number of peers to broadcast to - if direct.BitLen() == 0 { - direct = big.NewInt(1) + sqrtPeers := math.Sqrt(float64(h.peers.len())) // Approximate number of peers to broadcast to + + // Use some hysteresis to avoid oscillating between two values, stabilising the modulus in the peer selection + // If the number of peers is small, use a minimum of 1 peer + var directInt int64 + lastDirect := h.lastDirect.Load() + if int64(sqrtPeers) >= lastDirect { + directInt = max(int64(sqrtPeers), 1) + } else { + directInt = max(min(int64(sqrtPeers+directPeersHysteresis), lastDirect), 1) } - total := new(big.Int).Exp(direct, big.NewInt(2), nil) // Stabilise total peer count a bit based on sqrt peers + h.lastDirect.Store(directInt) + + direct := big.NewInt(directInt) // Number of peers to send directly to + total := big.NewInt(directInt * directInt) // Stabilise total peer count a bit based on sqrt peers var ( signer = types.LatestSigner(h.chain.Config()) // Don't care about chain status, we just need *a* sender