mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-21 20:26:41 +00:00
eth/txtracker: use lru.BasicLRU
This commit is contained in:
parent
2d7ebc5e4f
commit
f9a0352853
2 changed files with 20 additions and 40 deletions
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
|
@ -90,9 +91,8 @@ type TxInfo struct {
|
||||||
// when their transactions appear on chain.
|
// when their transactions appear on chain.
|
||||||
type Tracker struct {
|
type Tracker struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
txs map[common.Hash]*TxInfo // hash → per-tx state
|
txs lru.BasicLRU[common.Hash, *TxInfo] // tx hash -> tx info with lru eviction
|
||||||
peers map[string]*peerStats
|
peers map[string]*peerStats
|
||||||
order []common.Hash // insertion order for LRU eviction
|
|
||||||
|
|
||||||
chain Chain
|
chain Chain
|
||||||
lastFinalNum uint64 // last finalized block number processed
|
lastFinalNum uint64 // last finalized block number processed
|
||||||
|
|
@ -109,7 +109,7 @@ type Tracker struct {
|
||||||
// New creates a new tracker.
|
// New creates a new tracker.
|
||||||
func New() *Tracker {
|
func New() *Tracker {
|
||||||
return &Tracker{
|
return &Tracker{
|
||||||
txs: make(map[common.Hash]*TxInfo),
|
txs: lru.NewBasicLRU[common.Hash, *TxInfo](maxTracked),
|
||||||
peers: make(map[string]*peerStats),
|
peers: make(map[string]*peerStats),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
step: make(chan struct{}, 1),
|
step: make(chan struct{}, 1),
|
||||||
|
|
@ -159,27 +159,15 @@ func (t *Tracker) NotifyAccepted(peer string, hashes []common.Hash) {
|
||||||
|
|
||||||
addedAt := t.now()
|
addedAt := t.now()
|
||||||
for _, hash := range hashes {
|
for _, hash := range hashes {
|
||||||
if _, ok := t.txs[hash]; ok {
|
if t.txs.Contains(hash) {
|
||||||
continue // already tracked, keep first deliverer
|
continue // already tracked, keep first deliverer
|
||||||
}
|
}
|
||||||
t.txs[hash] = &TxInfo{Deliverer: peer, AddedAt: addedAt}
|
t.txs.Add(hash, &TxInfo{Deliverer: peer, AddedAt: addedAt})
|
||||||
t.order = append(t.order, hash)
|
|
||||||
}
|
}
|
||||||
// Ensure the delivering peer has a stats entry.
|
// Ensure the delivering peer has a stats entry.
|
||||||
if len(hashes) > 0 && t.peers[peer] == nil {
|
if len(hashes) > 0 && t.peers[peer] == nil {
|
||||||
t.peers[peer] = &peerStats{}
|
t.peers[peer] = &peerStats{}
|
||||||
}
|
}
|
||||||
// Evict oldest entries if over capacity.
|
|
||||||
for len(t.txs) > maxTracked {
|
|
||||||
oldest := t.order[0]
|
|
||||||
t.order = t.order[1:]
|
|
||||||
delete(t.txs, oldest)
|
|
||||||
}
|
|
||||||
// Compact the backing array when it grows too large. Reslicing
|
|
||||||
// with order[1:] doesn't free earlier slots in the array.
|
|
||||||
if cap(t.order) > 2*maxTracked {
|
|
||||||
t.order = append([]common.Hash(nil), t.order...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllPeerStats returns a snapshot of per-peer inclusion statistics.
|
// GetAllPeerStats returns a snapshot of per-peer inclusion statistics.
|
||||||
|
|
@ -238,7 +226,7 @@ func (t *Tracker) handleChainHead(ev core.ChainHeadEvent) {
|
||||||
blockHash := block.Hash()
|
blockHash := block.Hash()
|
||||||
blockIncl := make(map[string]int)
|
blockIncl := make(map[string]int)
|
||||||
for _, tx := range block.Transactions() {
|
for _, tx := range block.Transactions() {
|
||||||
ti, ok := t.txs[tx.Hash()]
|
ti, ok := t.txs.Peek(tx.Hash())
|
||||||
if !ok || ti.AddedAt >= blockTime {
|
if !ok || ti.AddedAt >= blockTime {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -294,13 +282,15 @@ func (t *Tracker) collectFinalizationCredits() map[string]int {
|
||||||
// "already credited in a prior pass" (BlockNum <= lastFinalNum); no
|
// "already credited in a prior pass" (BlockNum <= lastFinalNum); no
|
||||||
// separate status bookkeeping is needed.
|
// separate status bookkeeping is needed.
|
||||||
buckets := make(map[uint64][]*TxInfo)
|
buckets := make(map[uint64][]*TxInfo)
|
||||||
for _, ti := range t.txs {
|
for _, hash := range t.txs.Keys() {
|
||||||
if ti.BlockNum <= t.lastFinalNum || ti.BlockNum > finalNum {
|
ti, ok := t.txs.Peek(hash)
|
||||||
|
if !ok || ti.BlockNum <= t.lastFinalNum || ti.BlockNum > finalNum {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
buckets[ti.BlockNum] = append(buckets[ti.BlockNum], ti)
|
buckets[ti.BlockNum] = append(buckets[ti.BlockNum], ti)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
total := 0
|
||||||
for num, tis := range buckets {
|
for num, tis := range buckets {
|
||||||
canonHash := t.chain.GetCanonicalHash(num)
|
canonHash := t.chain.GetCanonicalHash(num)
|
||||||
if canonHash == (common.Hash{}) {
|
if canonHash == (common.Hash{}) {
|
||||||
|
|
@ -316,22 +306,15 @@ func (t *Tracker) collectFinalizationCredits() map[string]int {
|
||||||
}
|
}
|
||||||
if ti.Deliverer != "" {
|
if ti.Deliverer != "" {
|
||||||
credits[ti.Deliverer]++
|
credits[ti.Deliverer]++
|
||||||
|
total++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if total := sumCounts(credits); total > 0 {
|
if total > 0 {
|
||||||
log.Trace("Accumulated finalization credits",
|
log.Trace("Accumulated finalization credits",
|
||||||
"from", t.lastFinalNum+1, "to", finalNum, "txs", total)
|
"from", t.lastFinalNum+1, "to", finalNum, "txs", total)
|
||||||
}
|
}
|
||||||
t.lastFinalNum = finalNum
|
t.lastFinalNum = finalNum
|
||||||
return credits
|
return credits
|
||||||
}
|
}
|
||||||
|
|
||||||
func sumCounts(m map[string]int) int {
|
|
||||||
var sum int
|
|
||||||
for _, v := range m {
|
|
||||||
sum += v
|
|
||||||
}
|
|
||||||
return sum
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -182,22 +182,19 @@ func TestNotifyReceived(t *testing.T) {
|
||||||
t.Fatalf("expected zero stats before chain events, got %+v", ps)
|
t.Fatalf("expected zero stats before chain events, got %+v", ps)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal state: all tx→deliverer mappings recorded, insertion order
|
// Internal state: all tx→deliverer mappings recorded.
|
||||||
// preserved in the FIFO slice.
|
|
||||||
tr.mu.Lock()
|
tr.mu.Lock()
|
||||||
defer tr.mu.Unlock()
|
defer tr.mu.Unlock()
|
||||||
if len(tr.txs) != 3 {
|
if tr.txs.Len() != 3 {
|
||||||
t.Fatalf("expected 3 tracked txs, got %d", len(tr.txs))
|
t.Fatalf("expected 3 tracked txs, got %d", tr.txs.Len())
|
||||||
}
|
|
||||||
if len(tr.order) != 3 {
|
|
||||||
t.Fatalf("expected order length 3, got %d", len(tr.order))
|
|
||||||
}
|
}
|
||||||
for i, h := range hashes {
|
for i, h := range hashes {
|
||||||
if got := tr.txs[h]; got.Deliverer != "peerA" {
|
got, ok := tr.txs.Peek(h)
|
||||||
t.Fatalf("tx %d: expected deliverer=peerA, got %q", i, got.Deliverer)
|
if !ok {
|
||||||
|
t.Fatalf("tx %d: not tracked", i)
|
||||||
}
|
}
|
||||||
if tr.order[i] != h {
|
if got.Deliverer != "peerA" {
|
||||||
t.Fatalf("order[%d] mismatch", i)
|
t.Fatalf("tx %d: expected deliverer=peerA, got %q", i, got.Deliverer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue