Skip to content

BlockAssembly logs and resets as 'reorging' for forward-only catch-ups (gap of 2 blocks triggers Reset path) #898

@oskarszoon

Description

@oskarszoon

Summary

BlockAssembler.setBestBlockHeader treats any tip-jump of ≥ 2 blocks as a reorg, runs through handleReorg / subtreeProcessor.Reset(moveBack, moveForward, ...), and logs "best block header is not the same as the previous best block header, reorging". In practice the vast majority of these "reorgs" are not chain reorgs at all — they are forward-only catch-ups (moveBack = 0) where BA simply fell behind the tip while processing a heavy block.

Code

services/blockassembly/BlockAssembler.go:705-727:

switch {
case bestBlockchainBlockHeader.Hash().IsEqual(bestBlockAccordingToBlockAssembly.Hash()):
    // no-op (gap 0)
    return

case !bestBlockchainBlockHeader.HashPrevBlock.IsEqual(bestBlockAccordingToBlockAssembly.Hash()):
    // logs "reorging", calls handleReorg(...)
    ctxLogger.Infof("[BlockAssembler][%s] best block header is not the same as the previous best block header, reorging: %s", ...)
    b.setCurrentRunningState(StateReorging)
    err = b.handleReorg(ctx, bestBlockchainBlockHeader, bestBlockchainBlockHeaderMeta.Height)
    ...

default:
    // "moving up" — single-block forward (gap 1)
}

The middle branch fires whenever the new tip's HashPrevBlock != BA.head, which is anything from gap of 2 to gap of (CoinbaseMaturity - 1) blocks. At gap ≥ CoinbaseMaturity (100) BA does a full hard reset; below that it Resets via subtreeProcessor.Reset(moveBack=0, moveForward=N, ...) which loads N forward blocks' tx maps simultaneously.

Reproduction (production, today)

bsva-ovh-teranode-ttn-eu-4, mainnet legacy sync. Container CPU-capped to 1 core (separate quickstart issue) made it worse, but the path itself is independent.

Timeline:

12:38:10  BA: moveForwardBlock height 1605444 (txCount=192951, size=37.6 MB)
12:45:01  BA: DONE in 6m51s  (single block, processing subtrees into transaction map)
12:45:01  BA: tip now 1605463 — 19 blocks ahead. Logs "reorging".
12:45:15  BA: handleReorg moveBackBlocks=0, moveForwardBlocks=19 — DONE in 388 ms

Go memstats during the heavy block: HeapSys ≈ 10.8 GB, HeapReleased ≈ 10.2 GB. The Reset path loads all forward block tx maps at once.

Pattern: a single big block stalls BA → legacy keeps ingesting → tip drifts → BA logs "reorging" with moveBack=0.

Why this matters

  1. Misleading log line. Operators reading "reorging" assume a chain split. The actual condition is "BA is behind the tip by ≥ 2." A grep for "reorg" in logs no longer answers "did we reorg?".
  2. Wrong memory shape. The Reset path loads N tx maps simultaneously to support a moveBack rollback. For moveBack=0 there is nothing to roll back — iterating moveForwardBlock per block would have the same effect with bounded memory.
  3. Common case during sync. With 192k-tx blocks taking minutes to process, gap-of-2 is routine during legacy sync, not exceptional.

History

Not a regression — current behaviour was introduced in 91dec7f80 (2023-08-01). Before that, gap-of-2 just logged an error and continue'd (also wrong, just differently). The new handleReorg call replaced the error log; the assumption that "any gap = reorg" survived.

Proposed change

Split the middle branch by moveBack count, not by gap size:

case !bestBlockchainBlockHeader.HashPrevBlock.IsEqual(BA.head):
    moveBack, moveForward := computeMoves(...)
    if len(moveBack) == 0 {
        // pure catch-up — iterate forward, no Reset
        for _, blk := range moveForward {
            stp.moveForwardBlock(...)
        }
    } else {
        // real reorg with rollback — existing Reset path
        b.handleReorg(ctx, ...)
    }

Benefits:

  • Log says "catching up" vs "reorging" honestly.
  • No N-block-wide tx-map allocation when moveBack=0.
  • Real reorgs still take the existing Reset path.

Related

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions