Skip to content

Commit bd73007

Browse files
committed
go/worker/storage/committee: Fix stuck finalization
1 parent df385f8 commit bd73007

1 file changed

Lines changed: 21 additions & 20 deletions

File tree

go/worker/storage/committee/node.go

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ const (
6161
// maxInFlightRounds is the maximum number of rounds that should be fetched before waiting
6262
// for them to be applied.
6363
maxInFlightRounds = 100
64+
65+
// maxNonFinalizedRoots is the maximum number of allowed non-finalized roots.
66+
// It should be set lower than the limit configured by the underlying node database.
67+
maxNonFinalizedRoots = 1000
6468
)
6569

6670
type roundItem interface {
@@ -1101,22 +1105,21 @@ func (n *Node) worker() { // nolint: gocyclo
11011105
pendingApply := &minRoundPQ{}
11021106
pendingFinalize := &minRoundPQ{}
11031107

1104-
// Main processing loop. When a new block comes in, its state and io roots are inspected and their
1105-
// writelogs fetched from peers in case we don't have them locally yet. Fetches are
1106-
// asynchronous and, once complete, trigger local Apply operations. These are serialized
1107-
// per round (all applies for a given round have to be complete before applying anything for following
1108-
// rounds) using the fetched diffs and pending finalization priority queue. Once a round has all its write
1109-
// logs applied, a Finalize for it is triggered, again serialized by round but otherwise asynchronous
1110-
// (pendingFinalization and cachedLastRound).
1108+
// Main processing loop. When a new block arrives, its state and I/O roots are inspected.
1109+
// If missing locally, diffs are fetched from peers, possibly for many rounds in parallel,
1110+
// including all missing rounds since the last fully applied one. Fetched diffs are then applied
1111+
// in round order, ensuring no gaps. Once a round has all its roots applied, background finalization
1112+
// for that round is triggered asynchronously, not blocking concurrent fetching and diff application.
11111113
mainLoop:
11121114
for {
1113-
// Drain the Apply and Finalize queues first, before waiting for new events in the select
1114-
// below. Applies are drained first, followed by finalizations (which are asynchronous
1115-
// but serialized, i.e. only one Finalize can be in progress at a time).
1116-
1117-
// Apply any writelogs that came in through fetchDiff, but only if they are for the round
1118-
// after the last fully applied one (lastFullyAppliedRound).
1119-
if len(*pendingApply) > 0 && lastFullyAppliedRound+1 == (*pendingApply)[0].GetRound() {
1115+
// Drain the Apply and Finalize queues first, before waiting for new events in the select below.
1116+
1117+
// Apply fetched writelogs, but only if they are for the round after the last fully applied one
1118+
// and current number of pending roots to be finalized is smaller than max allowed.
1119+
applyNext := pendingApply.Len() > 0 &&
1120+
lastFullyAppliedRound+1 == (*pendingApply)[0].GetRound() &&
1121+
pendingFinalize.Len() < maxNonFinalizedRoots-1 // -1 since one may be finalizing (popped already)
1122+
if applyNext {
11201123
lastDiff := heap.Pop(pendingApply).(*fetchedDiff)
11211124
// Apply the write log if one exists.
11221125
err = nil
@@ -1173,15 +1176,13 @@ mainLoop:
11731176
continue
11741177
}
11751178

1176-
// Check if any new rounds were fully applied and need to be finalized. Only finalize
1177-
// if it's the round after the one that was finalized last (cachedLastRound).
1178-
// The finalization happens asynchronously with respect to this worker loop and any
1179-
// applies that happen for subsequent rounds (which can proceed while earlier rounds are
1180-
// still finalizing).
1179+
// Check if any new rounds were fully applied and need to be finalized.
1180+
// Only finalize if it's the round after the one that was finalized last.
1181+
// As a consequence at most one finalization can be happening at the time.
11811182
if len(*pendingFinalize) > 0 && cachedLastRound+1 == (*pendingFinalize)[0].GetRound() {
11821183
lastSummary := heap.Pop(pendingFinalize).(*blockSummary)
11831184
wg.Add(1)
1184-
go func() {
1185+
go func() { // don't block fetching and applying remaining rounds.
11851186
defer wg.Done()
11861187
n.finalize(lastSummary)
11871188
}()

0 commit comments

Comments
 (0)