From df517d1f3a2751591f19688e23d742790d4b200d Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Sat, 11 Apr 2026 16:35:54 +0200 Subject: [PATCH] eth/txtracker: replace time.Sleep with deterministic step channel Tests used time.Sleep(50ms) to wait for async chain head processing, making the suite slow (~2s) and flaky under CI load. Add a step channel (buffered 1) to the Tracker, sent after each event in the loop. Tests wait on the channel with a 1s timeout. Suite now completes in <10ms. --- eth/txtracker/tracker.go | 6 ++++++ eth/txtracker/tracker_test.go | 35 ++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/eth/txtracker/tracker.go b/eth/txtracker/tracker.go index 842d1776c3..b5ebc5ba7e 100644 --- a/eth/txtracker/tracker.go +++ b/eth/txtracker/tracker.go @@ -76,6 +76,7 @@ type Tracker struct { sub event.Subscription quit chan struct{} + step chan struct{} // test sync: sent after each event is processed wg sync.WaitGroup } @@ -85,6 +86,7 @@ func New() *Tracker { txs: make(map[common.Hash]string), peers: make(map[string]*peerStats), quit: make(chan struct{}), + step: make(chan struct{}, 1), } } @@ -171,6 +173,10 @@ func (t *Tracker) loop() { select { case ev := <-t.headCh: t.handleChainHead(ev) + select { + case t.step <- struct{}{}: + default: + } case <-t.sub.Err(): return case <-t.quit: diff --git a/eth/txtracker/tracker_test.go b/eth/txtracker/tracker_test.go index 22ddabf0e9..9274e70d26 100644 --- a/eth/txtracker/tracker_test.go +++ b/eth/txtracker/tracker_test.go @@ -96,9 +96,14 @@ func makeTx(nonce uint64) *types.Transaction { return types.NewTx(&types.LegacyTx{Nonce: nonce, GasPrice: big.NewInt(1), Gas: 21000}) } -// waitForHead gives the tracker time to process a chain head event. -func waitForHead() { - time.Sleep(50 * time.Millisecond) +// waitStep blocks until the tracker has processed one event. +func waitStep(t *testing.T, tr *Tracker) { + t.Helper() + select { + case <-tr.step: + case <-time.After(time.Second): + t.Fatal("timeout waiting for tracker step") + } } func TestNotifyReceived(t *testing.T) { @@ -132,7 +137,7 @@ func TestInclusionEMA(t *testing.T) { // Block 1 includes peerA's tx. chain.addBlock(1, []*types.Transaction{tx}) chain.sendHead(1) - waitForHead() + waitStep(t, tr) stats := tr.GetAllPeerStats() if stats["peerA"].RecentIncluded <= 0 { @@ -143,7 +148,7 @@ func TestInclusionEMA(t *testing.T) { // Block 2 has no txs from peerA — EMA should decay. chain.addBlock(2, nil) chain.sendHead(2) - waitForHead() + waitStep(t, tr) stats = tr.GetAllPeerStats() if stats["peerA"].RecentIncluded >= ema1 { @@ -163,7 +168,7 @@ func TestFinalization(t *testing.T) { // Include in block 1. chain.addBlock(1, []*types.Transaction{tx}) chain.sendHead(1) - waitForHead() + waitStep(t, tr) // Not finalized yet. stats := tr.GetAllPeerStats() @@ -175,7 +180,7 @@ func TestFinalization(t *testing.T) { chain.setFinalBlock(1) chain.addBlock(2, nil) chain.sendHead(2) - waitForHead() + waitStep(t, tr) stats = tr.GetAllPeerStats() if stats["peerA"].Finalized != 1 { @@ -197,13 +202,13 @@ func TestMultiplePeers(t *testing.T) { // Both included in block 1. chain.addBlock(1, []*types.Transaction{tx1, tx2}) chain.sendHead(1) - waitForHead() + waitStep(t, tr) // Finalize. chain.setFinalBlock(1) chain.addBlock(2, nil) chain.sendHead(2) - waitForHead() + waitStep(t, tr) stats := tr.GetAllPeerStats() if stats["peerA"].Finalized != 1 { @@ -226,12 +231,12 @@ func TestFirstDelivererWins(t *testing.T) { chain.addBlock(1, []*types.Transaction{tx}) chain.sendHead(1) - waitForHead() + waitStep(t, tr) chain.setFinalBlock(1) chain.addBlock(2, nil) chain.sendHead(2) - waitForHead() + waitStep(t, tr) stats := tr.GetAllPeerStats() if stats["peerA"].Finalized != 1 { @@ -254,12 +259,12 @@ func TestNoFinalizationCredit(t *testing.T) { // Include but don't finalize. chain.addBlock(1, []*types.Transaction{tx}) chain.sendHead(1) - waitForHead() + waitStep(t, tr) // Send more heads without finalization. chain.addBlock(2, nil) chain.sendHead(2) - waitForHead() + waitStep(t, tr) stats := tr.GetAllPeerStats() if stats["peerA"].Finalized != 0 { @@ -279,13 +284,13 @@ func TestEMADecay(t *testing.T) { // Include in block 1. chain.addBlock(1, []*types.Transaction{tx}) chain.sendHead(1) - waitForHead() + waitStep(t, tr) // Send 30 empty blocks — EMA should decay close to zero. for i := uint64(2); i <= 31; i++ { chain.addBlock(i, nil) chain.sendHead(i) - waitForHead() + waitStep(t, tr) } stats := tr.GetAllPeerStats()