From 6a3d6decb391f099801f0ea2691fdee8863cbf01 Mon Sep 17 00:00:00 2001 From: vietddude Date: Fri, 10 Jul 2026 23:33:32 +0700 Subject: [PATCH] fix(worker): don't queue catchup from genesis on cold start Since PR #102, GetLatestBlock returns (0, nil) for a missing key instead of an error. CatchupWorker.loadCatchupProgress still treated 0 as a real indexed block, so starting a new chain queued a catchup range from block 1 to chain head (millions of sub-ranges) and hung batch-saving them. Skip range creation when latest == 0: a cold start has no prior progress to catch up from; the regular worker anchors on chain head instead. Fixes #104 --- internal/worker/catchup.go | 4 ++- internal/worker/regular_test.go | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/internal/worker/catchup.go b/internal/worker/catchup.go index f544d4b..803eba9 100644 --- a/internal/worker/catchup.go +++ b/internal/worker/catchup.go @@ -173,7 +173,9 @@ func (cw *CatchupWorker) loadCatchupProgress() []blockstore.CatchupRange { // Only create a new range if no existing ranges found if len(ranges) == 0 { - if latest, err1 := cw.blockStore.GetLatestBlock(cw.chain.GetNetworkInternalCode()); err1 == nil { + // latest == 0 means cold start (GetLatestBlock returns (0, nil) for a + // missing key) — queueing 1..head would re-index the whole chain. + if latest, err1 := cw.blockStore.GetLatestBlock(cw.chain.GetNetworkInternalCode()); err1 == nil && latest > 0 { if chainHead, err2 := cw.chain.GetLatestBlockNumber(cw.ctx); err2 == nil { head := cw.processingHead(chainHead) if head <= latest { diff --git a/internal/worker/regular_test.go b/internal/worker/regular_test.go index 859b821..2b9f89c 100644 --- a/internal/worker/regular_test.go +++ b/internal/worker/regular_test.go @@ -571,3 +571,47 @@ func (s *stubBlockStore) SaveBlockHashes(string, []blockstore.BlockHashEntry) er func (s *stubBlockStore) Close() error { return nil } + +func TestCatchupWorkerLoadCatchupProgressColdStartQueuesNothing(t *testing.T) { + t.Parallel() + + // Cold start: GetLatestBlock returns (0, nil). There is no prior progress, + // so no catchup range must be created (regression: issue #104 queued + // 1..head and re-indexed the whole chain). + store := &stubBlockStore{latestBlock: 0} + cw := &CatchupWorker{ + BaseWorker: &BaseWorker{ + ctx: context.Background(), + cancel: func() {}, + logger: slog.New(slog.NewTextHandler(io.Discard, nil)), + chain: &stubIndexer{name: "bsc", internalCode: "BSC", networkType: enum.NetworkTypeEVM, latest: 109195919}, + blockStore: store, + statusRegistry: status.NewRegistry(), + }, + } + + require.Empty(t, cw.loadCatchupProgress()) + require.Empty(t, store.savedCatchupRanges) +} + +func TestCatchupWorkerLoadCatchupProgressResumeQueuesGap(t *testing.T) { + t.Parallel() + + // Prior progress exists: the gap from KV latest to chain head is queued. + store := &stubBlockStore{latestBlock: 100} + cw := &CatchupWorker{ + BaseWorker: &BaseWorker{ + ctx: context.Background(), + cancel: func() {}, + logger: slog.New(slog.NewTextHandler(io.Discard, nil)), + chain: &stubIndexer{name: "bsc", internalCode: "BSC", networkType: enum.NetworkTypeEVM, latest: 150}, + blockStore: store, + statusRegistry: status.NewRegistry(), + }, + } + + ranges := cw.loadCatchupProgress() + require.NotEmpty(t, ranges) + require.Equal(t, uint64(101), ranges[0].Start) + require.Equal(t, uint64(150), ranges[len(ranges)-1].End) +}