Skip to content

Commit 7dcd003

Browse files
lystopaddomiwei
andauthored
[r3.5] cl/phase1/forkchoice: fix block_importing_latency never recording on Fulu+ (#21843)
Cherry-pick of #21842 to release/3.5. `block_importing_latency` was only recorded from a call nested in the Deneb-only blob data-availability branch of `OnBlock`, so it never fired on Fulu+ (current mainnet) and read a flat 0 in Grafana. Moved to the fork-agnostic `highestSeen` tip-advance so it records once per new head block on all forks. See #21842 for full analysis. Clean cherry-pick. --------- Co-authored-by: kewei <kewei.train@gmail.com>
1 parent ad6dc2f commit 7dcd003

2 files changed

Lines changed: 53 additions & 6 deletions

File tree

cl/phase1/forkchoice/on_block.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,8 @@ func verifyKzgCommitmentsAgainstTransactions(cfg *clparams.BeaconChainConfig, bl
8080
return misc.ValidateBlobs(block.Body.ExecutionPayload.BlobGasUsed, cfg.MaxBlobGasPerBlock, maxBlobsPerBlock, expectedBlobHashes, &transactions)
8181
}
8282

83-
func collectOnBlockLatencyToUnixTime(ethClock eth_clock.EthereumClock, slot uint64) {
84-
currSlot := ethClock.GetCurrentSlot()
85-
if slot != currSlot {
83+
func collectOnBlockLatencyToUnixTime(ethClock eth_clock.EthereumClock, slot, currentSlotOnEntry uint64) {
84+
if slot != currentSlotOnEntry {
8685
return
8786
}
8887
initialSlotTime := ethClock.GetSlotTime(slot)
@@ -127,6 +126,7 @@ func (f *ForkChoiceStore) OnBlock(ctx context.Context, block *cltypes.SignedBeac
127126
if ancestorNode := f.Ancestor(block.Block.ParentRoot, finalizedSlot); ancestorNode.Root != finalizedCheckpoint.Root {
128127
return ErrNotFinalizedDescendant
129128
}
129+
currentSlotOnEntry := f.ethClock.GetCurrentSlot()
130130

131131
// Validate parent payload status path early (before expensive operations)
132132
blockEpoch := f.computeEpochAtSlot(block.Block.Slot)
@@ -192,9 +192,6 @@ func (f *ForkChoiceStore) OnBlock(ctx context.Context, block *cltypes.SignedBeac
192192
}
193193
return fmt.Errorf("OnBlock: data is not available for block %x: %v", common.Hash(blockRoot), err)
194194
}
195-
if f.highestSeen.Load() < block.Block.Slot {
196-
collectOnBlockLatencyToUnixTime(f.ethClock, block.Block.Slot)
197-
}
198195
}
199196
}
200197

@@ -261,6 +258,7 @@ func (f *ForkChoiceStore) OnBlock(ctx context.Context, block *cltypes.SignedBeac
261258
if block.Block.Slot > f.highestSeen.Load() {
262259
f.highestSeen.Store(block.Block.Slot)
263260
f.highestSeenRoot.Store(common.Hash(blockRoot))
261+
collectOnBlockLatencyToUnixTime(f.ethClock, block.Block.Slot, currentSlotOnEntry)
264262
}
265263
startStateProcess := time.Now()
266264

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2026 The Erigon Authors
2+
// This file is part of Erigon.
3+
//
4+
// Erigon is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// Erigon is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with Erigon. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package forkchoice
18+
19+
import (
20+
"testing"
21+
"time"
22+
23+
"github.com/stretchr/testify/require"
24+
"go.uber.org/mock/gomock"
25+
26+
"github.com/erigontech/erigon/cl/utils/eth_clock"
27+
"github.com/erigontech/erigon/diagnostics/metrics"
28+
)
29+
30+
// block_importing_latency must be recorded for a block imported in its own
31+
// (current) slot, and skipped for older blocks (e.g. backfill), so the gauge
32+
// reflects head-arrival latency rather than block age.
33+
func TestCollectOnBlockLatency(t *testing.T) {
34+
ctrl := gomock.NewController(t)
35+
clock := eth_clock.NewMockEthereumClock(ctrl)
36+
g := metrics.GetOrCreateGauge("block_importing_latency")
37+
38+
// A non-current-slot block (e.g. backfill) must not update the metric.
39+
g.Set(-1)
40+
collectOnBlockLatencyToUnixTime(clock, 99, 100)
41+
require.Equal(t, float64(-1), g.GetValue(), "non-current-slot block must not update the metric")
42+
43+
// A current-slot block must record a non-negative latency.
44+
clock.EXPECT().GetSlotTime(uint64(100)).Return(time.Now().Add(-1500 * time.Millisecond))
45+
collectOnBlockLatencyToUnixTime(clock, 100, 100)
46+
v := g.GetValue()
47+
require.NotEqual(t, float64(-1), v, "current-slot block must update the metric")
48+
require.GreaterOrEqual(t, v, float64(0))
49+
}

0 commit comments

Comments
 (0)