From 1814a552df4478090000ebc4a149e585e4533227 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Mon, 11 May 2026 10:37:36 -0400 Subject: [PATCH] fix: skip fetching transactions already in cache Filter out transaction hashes that are already cached before sending GetPooledTransactions requests. This avoids redundant network fetches when multiple peers announce the same transactions. --- p2p/conns.go | 6 ++++++ p2p/datastructures/lru.go | 29 +++++++++++++++++++++++++++++ p2p/protocol.go | 10 ++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/p2p/conns.go b/p2p/conns.go index 9a3c51f95..d4900c963 100644 --- a/p2p/conns.go +++ b/p2p/conns.go @@ -561,6 +561,12 @@ func (c *Conns) PeekTxsWithHashes(hashes []common.Hash) ([]common.Hash, []*types return c.txs.PeekManyWithKeys(hashes) } +// FilterUnknownTxHashes returns only the hashes that are NOT in the transaction cache. +// Uses a single read lock for efficient batch checking. +func (c *Conns) FilterUnknownTxHashes(hashes []common.Hash) []common.Hash { + return c.txs.FilterMissing(hashes) +} + // Blocks returns the global blocks cache. func (c *Conns) Blocks() *ds.LRU[common.Hash, BlockCache] { return c.blocks diff --git a/p2p/datastructures/lru.go b/p2p/datastructures/lru.go index c38ce0315..94185a1e8 100644 --- a/p2p/datastructures/lru.go +++ b/p2p/datastructures/lru.go @@ -189,6 +189,35 @@ func (c *LRU[K, V]) PeekManyWithKeys(keys []K) ([]K, []V) { return foundKeys, foundValues } +// FilterMissing returns keys that are NOT present in the cache (or are expired). +// Uses a single read lock for all lookups. +func (c *LRU[K, V]) FilterMissing(keys []K) []K { + if len(keys) == 0 { + return nil + } + + c.mu.RLock() + defer c.mu.RUnlock() + + now := time.Now() + missing := make([]K, 0, len(keys)) + + for _, key := range keys { + elem, ok := c.items[key] + if !ok { + missing = append(missing, key) + continue + } + + e := elem.Value.(*entry[K, V]) + if e.expiresAt != nil && now.After(*e.expiresAt) { + missing = append(missing, key) + } + } + + return missing +} + // Update atomically updates a value in the cache using the provided update function. // The update function receives the current value (or zero value if not found) and // returns the new value to store. Returns true if the key already existed (and was diff --git a/p2p/protocol.go b/p2p/protocol.go index 6debdba20..3912b2187 100644 --- a/p2p/protocol.go +++ b/p2p/protocol.go @@ -1251,8 +1251,14 @@ func (c *conn) handleNewPooledTransactionHashes(version uint, msg ethp2p.Msg) er return nil } - request := ð.GetPooledTransactionsPacket{GetPooledTransactionsRequest: hashes} - c.countMsgSent(request.Name(), float64(len(hashes))) + // Filter out transactions we already have in cache to avoid redundant fetches. + unknown := c.conns.FilterUnknownTxHashes(hashes) + if len(unknown) == 0 { + return nil + } + + request := ð.GetPooledTransactionsPacket{GetPooledTransactionsRequest: unknown} + c.countMsgSent(request.Name(), float64(len(unknown))) return ethp2p.Send(c.rw, eth.GetPooledTransactionsMsg, request) }