Skip to content
This repository was archived by the owner on Nov 25, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6700e62
fix(vmsync): log context cancellation as INFO instead of ERROR during…
powerslider Nov 17, 2025
3b38cf0
fix(vmsync): improve on remarks
powerslider Nov 17, 2025
83315e1
Merge branch 'master' into powerslider/1410-context-cancel-misleading…
powerslider Nov 17, 2025
bbe4384
fix: remove checking if error is wrapped
powerslider Nov 17, 2025
3ac161f
style: improvements
powerslider Nov 17, 2025
00e8084
Merge branch 'master' into powerslider/1410-context-cancel-misleading…
powerslider Nov 17, 2025
26f9713
fix: add context cancellation check to block syncer loop
powerslider Nov 18, 2025
e953c21
fix(statesync): add context cancellation checks to prevent shutdown hang
powerslider Nov 18, 2025
ee80793
Merge branch 'master' into powerslider/1410-context-cancel-misleading…
powerslider Nov 19, 2025
23ab197
fix: remove per-select context error checks
powerslider Nov 19, 2025
d7ec158
test(vmsync): simplify test helpers and remove redundant cancellation…
powerslider Nov 19, 2025
2219e78
fix(statesync): remove pre-select context err check in code_queue.go
powerslider Nov 19, 2025
26a2c5a
Merge branch 'master' into powerslider/1410-context-cancel-misleading…
powerslider Nov 19, 2025
7fc9a87
Merge branch 'master' into powerslider/1410-context-cancel-misleading…
powerslider Nov 20, 2025
c3e1590
fix: small remarks
powerslider Nov 20, 2025
b0565c7
Merge branch 'master' into powerslider/1410-context-cancel-misleading…
powerslider Nov 20, 2025
cf30dfe
fix(statesync): add context cancellation check in storageTrieTask.OnL…
powerslider Nov 21, 2025
ee19498
fix(blocksync): remove redundant context checks
powerslider Nov 21, 2025
5131d0c
feat(statesync): introduce Finalizer interface for syncer cleanup
powerslider Nov 21, 2025
626b3fb
Merge branch 'powerslider/1410-context-cancel-misleading-logs' into p…
powerslider Nov 21, 2025
69fb806
fix(registry): ctx shadowing
powerslider Nov 21, 2025
c0d0e5b
Merge branch 'powerslider/1410-context-cancel-misleading-logs' into p…
powerslider Nov 21, 2025
c21d924
Merge branch 'master' into powerslider/1089-finalize-syncers
powerslider Nov 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion plugin/evm/atomic/sync/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const (
var (
_ sync.Syncer = (*Syncer)(nil)
_ syncclient.LeafSyncTask = (*syncerLeafTask)(nil)
_ sync.Finalizer = (*Syncer)(nil)

errTargetHeightRequired = errors.New("target height must be > 0")
)
Expand Down Expand Up @@ -125,7 +126,6 @@ func NewSyncer(client syncclient.LeafClient, db *versiondb.Database, atomicTrie
syncer.syncer = syncclient.NewCallbackLeafSyncer(client, tasks, &syncclient.LeafSyncerConfig{
RequestSize: cfg.requestSize,
NumWorkers: cfg.numWorkers,
OnFailure: func() {}, // No-op since we flush progress to disk at the regular commit interval.
})

return syncer, nil
Expand All @@ -146,6 +146,16 @@ func (s *Syncer) Sync(ctx context.Context) error {
return s.syncer.Sync(ctx)
}

// Finalize commits any pending database changes to disk.
// This ensures that even if the sync is cancelled or fails, we preserve
// the progress up to the last fully synced height.
func (s *Syncer) Finalize() error {
if s.db == nil {
Copy link
Contributor

@alarso16 alarso16 Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would db be nil?

return nil
}
return s.db.Commit()
}

// addZeroes returns the big-endian representation of `height`, prefixed with [common.HashLength] zeroes.
func addZeroes(height uint64) []byte {
// Key format is [height(8 bytes)][blockchainID(32 bytes)]. Start should be the
Expand Down
16 changes: 16 additions & 0 deletions plugin/evm/vmsync/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ func (r *SyncerRegistry) Register(syncer syncpkg.Syncer) error {

// RunSyncerTasks executes all registered syncers synchronously.
func (r *SyncerRegistry) RunSyncerTasks(ctx context.Context, summary message.Syncable) error {
// Ensure finalization runs regardless of how this function exits.
// This guarantees cleanup even on early returns or panics.
defer r.FinalizeAll(summary)

// Early return if context is already canceled (e.g., during shutdown).
if err := ctx.Err(); err != nil {
return err
Expand Down Expand Up @@ -102,3 +106,15 @@ func (r *SyncerRegistry) StartAsync(ctx context.Context, summary message.Syncabl

return g
}

// FinalizeAll iterates over all registered syncers and calls Finalize on those that implement the Finalizer interface.
// Errors are logged but not returned to ensure best-effort cleanup of all syncers.
func (r *SyncerRegistry) FinalizeAll(summary message.Syncable) {
for _, task := range r.syncers {
if f, ok := task.syncer.(syncpkg.Finalizer); ok {
if err := f.Finalize(); err != nil {
log.Error("failed to finalize syncer", "syncer", task.name, "err", err, "summary", summary.GetBlockHash().Hex(), "height", summary.Height())
}
}
}
}
9 changes: 4 additions & 5 deletions sync/client/leaf_syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ type LeafSyncTask interface {
type LeafSyncerConfig struct {
RequestSize uint16 // Number of leafs to request from a peer at a time
NumWorkers int // Number of workers to process leaf sync tasks
OnFailure func() // Callback for handling errors during sync
}

type CallbackLeafSyncer struct {
Expand Down Expand Up @@ -159,9 +158,9 @@ func (c *CallbackLeafSyncer) Sync(ctx context.Context) error {
})
}

err := eg.Wait()
if err != nil {
c.config.OnFailure()
if err := eg.Wait(); err != nil {
return err
}
return err

return nil
Comment on lines +161 to +165
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if err := eg.Wait(); err != nil {
return err
}
return err
return nil
return eg.Wait()

}
4 changes: 4 additions & 0 deletions sync/statesync/code_queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ import (
"github.com/ava-labs/libevm/libevm/options"

"github.com/ava-labs/coreth/plugin/evm/customrawdb"

syncpkg "github.com/ava-labs/coreth/sync"
)

const defaultQueueCapacity = 5000

var (
_ syncpkg.Finalizer = (*CodeQueue)(nil)

errFailedToAddCodeHashesToQueue = errors.New("failed to add code hashes to queue")
errFailedToFinalizeCodeQueue = errors.New("failed to finalize code queue")
)
Expand Down
13 changes: 6 additions & 7 deletions sync/statesync/state_syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ func NewSyncer(client syncclient.Client, db ethdb.Database, root common.Hash, co
ss.syncer = syncclient.NewCallbackLeafSyncer(client, ss.segments, &syncclient.LeafSyncerConfig{
RequestSize: leafsRequestSize,
NumWorkers: defaultNumWorkers,
OnFailure: ss.onSyncFailure,
})

if codeQueue == nil {
Expand Down Expand Up @@ -301,19 +300,19 @@ func (t *stateSync) removeTrieInProgress(root common.Hash) (int, error) {
return len(t.triesInProgress), nil
}

// onSyncFailure is called if the sync fails, this writes all
// batches of in-progress trie segments to disk to have maximum
// progress to restore.
func (t *stateSync) onSyncFailure() {
// Finalize checks if there are any in-progress tries and flushes their batches to disk
// to preserve progress. This is called by the syncer registry on sync failure or cancellation.
func (t *stateSync) Finalize() error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based off the name alone, it seems like this shouldn't occur on success

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ok, but you should update the comments

t.lock.RLock()
defer t.lock.RUnlock()

for _, trie := range t.triesInProgress {
for _, segment := range trie.segments {
if err := segment.batch.Write(); err != nil {
log.Error("failed to write segment batch on sync failure", "err", err)
return
log.Error("failed to write segment batch on finalize", "err", err)
return err
}
}
}
return nil
}
7 changes: 7 additions & 0 deletions sync/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ type Syncer interface {
ID() string
}

// Finalizer provides a mechanism to perform cleanup operations after a sync operation.
// This is useful for handling inflight requests, flushing to disk, or other cleanup tasks.
type Finalizer interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's interesting you made this a separate interface. I totally agree that we shouldn't force all syncers to adhere to this, but should we instead make it something like:

```suggestion
type DynamicSyncer interface {
     Syncer
     UpdateSyncTarget(???) error

I'm honestly not sure, what do you think? My only concern with the current approach is that it seems unintuitive to check the types all the time, and rather we would expect the caller to either dynamic sync everything or static sync everything, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually now agree we this. This seems necessary for the state syncer, for example. Should it take in an error? (e.g. handle the cleanup different in the error vs no error case?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think you should add this sort of comment on the syncer registry, since it doesn't say it will call finalize

// Finalize performs any necessary cleanup operations.
Finalize() error
}

// SummaryProvider is an interface for providing state summaries.
type SummaryProvider interface {
StateSummaryAtBlock(ethBlock *types.Block) (block.StateSummary, error)
Expand Down
Loading