|
1 | 1 | package p2p |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "math/big" |
5 | 6 | "sync" |
6 | 7 | "sync/atomic" |
@@ -35,6 +36,7 @@ type ConnsOptions struct { |
35 | 36 | ShouldBroadcastTxHashes bool |
36 | 37 | ShouldBroadcastBlocks bool |
37 | 38 | ShouldBroadcastBlockHashes bool |
| 39 | + BroadcastWorkers int |
38 | 40 | } |
39 | 41 |
|
40 | 42 | // Conns manages a collection of active peer connections for transaction broadcasting. |
@@ -94,7 +96,15 @@ func NewConns(opts ConnsOptions) *Conns { |
94 | 96 | shouldBroadcastBlockHashes: opts.ShouldBroadcastBlockHashes, |
95 | 97 | txBroadcastCh: make(chan types.Transactions, 100000), |
96 | 98 | } |
97 | | - go c.txBroadcastLoop() |
| 99 | + |
| 100 | + workers := opts.BroadcastWorkers |
| 101 | + if workers <= 0 { |
| 102 | + workers = 4 |
| 103 | + } |
| 104 | + for i := 0; i < workers; i++ { |
| 105 | + go c.txBroadcastLoop() |
| 106 | + } |
| 107 | + |
98 | 108 | return c |
99 | 109 | } |
100 | 110 |
|
@@ -212,44 +222,48 @@ func (c *Conns) BroadcastTxs(txs types.Transactions) int { |
212 | 222 |
|
213 | 223 | // broadcastTxs sends RPC-submitted transactions to all peers via TransactionsMsg. |
214 | 224 | // Used by txBroadcastLoop to process transactions from EnqueueTxBroadcast. |
215 | | -// Has a timeout to prevent slow peers from blocking the broadcast loop. |
216 | | -func (c *Conns) broadcastTxs(txs types.Transactions, hashes []common.Hash, peers []*conn) int { |
| 225 | +func (c *Conns) broadcastTxs(txs types.Transactions, hashes []common.Hash, peers []*conn) { |
217 | 226 | rawList, err := rlp.EncodeToRawList([]*types.Transaction(txs)) |
218 | 227 | if err != nil { |
219 | 228 | log.Debug().Err(err).Msg("Failed to encode transactions") |
220 | | - return 0 |
| 229 | + return |
221 | 230 | } |
222 | 231 | packet := ð.TransactionsPacket{RawList: rawList} |
223 | 232 |
|
224 | | - var count atomic.Int32 |
225 | | - var wg sync.WaitGroup |
| 233 | + // Pre-encode the entire message once to avoid re-encoding for each peer. |
| 234 | + encodedMsg, err := rlp.EncodeToBytes(packet) |
| 235 | + if err != nil { |
| 236 | + log.Debug().Err(err).Msg("Failed to encode message") |
| 237 | + return |
| 238 | + } |
| 239 | + msgSize := uint32(len(encodedMsg)) |
| 240 | + |
| 241 | + // Cache loop-invariant values |
| 242 | + msgName := packet.Name() |
| 243 | + txCount := float64(len(txs)) |
| 244 | + rebroadcasting := c.shouldBroadcastTx || c.shouldBroadcastTxHashes |
226 | 245 |
|
227 | 246 | for _, peer := range peers { |
228 | | - wg.Go(func() { |
229 | | - peer.countMsgSent(packet.Name(), float64(len(txs))) |
230 | | - if err := ethp2p.Send(peer.rw, eth.TransactionsMsg, packet); err != nil { |
| 247 | + go func(peer *conn) { |
| 248 | + peer.countMsgSent(msgName, txCount) |
| 249 | + |
| 250 | + // Use WriteMsg directly with pre-encoded bytes instead of Send() |
| 251 | + msg := ethp2p.Msg{ |
| 252 | + Code: eth.TransactionsMsg, |
| 253 | + Size: msgSize, |
| 254 | + Payload: bytes.NewReader(encodedMsg), |
| 255 | + } |
| 256 | + if err := peer.rw.WriteMsg(msg); err != nil { |
231 | 257 | peer.logger.Debug().Err(err).Msg("Failed to send transactions") |
232 | 258 | return |
233 | 259 | } |
234 | | - peer.addKnownTxHashes(hashes) |
235 | | - count.Add(1) |
236 | | - }) |
237 | | - } |
238 | | - |
239 | | - // Wait with timeout |
240 | | - done := make(chan struct{}) |
241 | | - go func() { |
242 | | - wg.Wait() |
243 | | - close(done) |
244 | | - }() |
245 | 260 |
|
246 | | - select { |
247 | | - case <-done: |
248 | | - case <-time.After(5 * time.Second): |
249 | | - log.Warn().Int("peers", len(peers)).Msg("Broadcast timed out") |
| 261 | + // Only track known hashes if rebroadcasting is enabled |
| 262 | + if rebroadcasting { |
| 263 | + peer.addKnownTxHashes(hashes) |
| 264 | + } |
| 265 | + }(peer) |
250 | 266 | } |
251 | | - |
252 | | - return int(count.Load()) |
253 | 267 | } |
254 | 268 |
|
255 | 269 | // txBroadcastLoop is the worker that drains the broadcast channel and sends |
@@ -297,7 +311,7 @@ func (c *Conns) pullTxBatch() (types.Transactions, []common.Hash) { |
297 | 311 | } |
298 | 312 |
|
299 | 313 | // Drain more until max size or timeout |
300 | | - timer := time.NewTimer(time.Second) |
| 314 | + timer := time.NewTimer(100 * time.Millisecond) |
301 | 315 | defer timer.Stop() |
302 | 316 |
|
303 | 317 | for batchSize < maxTxPacketSize { |
|
0 commit comments