Skip to content

Commit

Permalink
block_test.go: FinalizeFastSync tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mitjat committed Aug 24, 2023
1 parent 0f7eacf commit 935c55d
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 7 deletions.
2 changes: 2 additions & 0 deletions analyzer/block/block.go
Expand Up @@ -246,10 +246,12 @@ func (b *blockBasedAnalyzer) ensureSlowSyncPrerequisites(ctx context.Context) (o
return false
}
if !precededBySlowSync {
b.logger.Error("finalizing the work of previous fast-sync analyzer(s)", "last_fast_sync_height", maxProcessedHeight)
if err := b.processor.FinalizeFastSync(ctx, maxProcessedHeight); err != nil {
b.logger.Error("failed to finalize the fast-sync phase (i.e. download genesis or similar)", "err", err)
return false
}
b.logger.Error("fast-sync finalization complete; proceeding with regular slow-sync analysis")
}

return true
Expand Down
76 changes: 69 additions & 7 deletions analyzer/block/block_test.go
Expand Up @@ -39,12 +39,17 @@ var testFastSyncBlockBasedConfig = &config.BlockBasedAnalyzerConfig{
}

type mockProcessor struct {
name string
processedBlocks map[uint64]struct{}
processedOrder []uint64
storage storage.TargetStorage
name string
storage storage.TargetStorage
isFastSync bool // set implicitly by setupAnalyzer()

// If specified, can simulate a failure at a given block height.
fail func(uint64) error

// Fields for testing; these let us report back what blocks were processed.
processedBlocks map[uint64]struct{}
processedOrder []uint64
fastSyncFinalizedAt *int64 // Height at which FinalizeFastSync was called, if any.
}

// PreWork implements block.BlockProcessor.
Expand All @@ -53,7 +58,8 @@ func (*mockProcessor) PreWork(ctx context.Context) error {
}

// PreWork implements block.BlockProcessor.
func (*mockProcessor) FinalizeFastSync(ctx context.Context, lastFastSyncHeight int64) error {
func (m *mockProcessor) FinalizeFastSync(ctx context.Context, lastFastSyncHeight int64) error {
m.fastSyncFinalizedAt = &lastFastSyncHeight
return nil
}

Expand All @@ -75,7 +81,7 @@ func (m *mockProcessor) ProcessBlock(ctx context.Context, height uint64) error {
queries.IndexingProgress,
height,
m.name,
false, // is_fast_sync
m.isFastSync,
)
if err != nil {
return err
Expand All @@ -101,8 +107,11 @@ func setupDB(t *testing.T) *postgres.Client {
}

func setupAnalyzer(t *testing.T, testDb *postgres.Client, p *mockProcessor, cfg *config.BlockBasedAnalyzerConfig, mode analyzer.BlockAnalysisMode) analyzer.Analyzer {
// Modify the processor in-place (!): make sure the isFastSync field is in agreement with the analyzer's mode.
p.isFastSync = (mode == analyzer.FastSyncMode)

// Initialize the block analyzer.
logger, err := log.NewLogger(fmt.Sprintf("test-analyzer-%s", p.name), os.Stdout, log.FmtJSON, log.LevelError)
logger, err := log.NewLogger(fmt.Sprintf("test-analyzer-%s", p.name), os.Stdout, log.FmtJSON, log.LevelInfo)
require.NoError(t, err, "log.NewLogger")
var blockRange config.BlockRange
switch mode {
Expand Down Expand Up @@ -426,3 +435,56 @@ func TestDistinctSlowSyncBlockAnalyzers(t *testing.T) {
}
}
}

func TestFinalizeFastSync(t *testing.T) {
// Test that a slow-sync analyzer finalizes the work of the preceding fast-sync analyzers, if any.
ctx := context.Background()
db := setupDB(t)

// Run multiple analyzers, each on a separate block range, to simulate past Nexus invocations.
// Note: The .Start() call blocks until the analyzer finishes.
p := &mockProcessor{name: "consensus", storage: db}
setupAnalyzer(t, db, p, &config.BlockBasedAnalyzerConfig{From: 1, To: 10, FastSync: &config.FastSyncConfig{To: 10}}, analyzer.FastSyncMode).Start(ctx)
require.Nil(t, p.fastSyncFinalizedAt,
fmt.Sprintf("fast-sync analyzer should never finalize fast-sync, but it did at %d", p.fastSyncFinalizedAt))

p = &mockProcessor{name: "consensus", storage: db}
setupAnalyzer(t, db, p, &config.BlockBasedAnalyzerConfig{From: 5, To: 20}, analyzer.SlowSyncMode).Start(ctx)
require.NotNil(t, p.fastSyncFinalizedAt,
"slow-sync analyzer should have finalized fast sync because it's taking up work from a fast-sync analyzer")
require.Equal(t, int64(10), *p.fastSyncFinalizedAt,
"slow-sync analyzer should finalize fast-sync at the height of the last fast-processed block")

p = &mockProcessor{name: "consensus", storage: db}
setupAnalyzer(t, db, p, &config.BlockBasedAnalyzerConfig{From: 21, To: 30}, analyzer.SlowSyncMode).Start(ctx)
require.Nil(t, p.fastSyncFinalizedAt,
"sencond slow-sync analyzer should not finalize fast-sync because its range extends an existing slow-sync-analyzed range")
}

func TestRefuseSlowSyncOnDirtyRange(t *testing.T) {
// Test that slow-sync analyzer won't start if the already-analyzed block range is non-contiguous.
ctx := context.Background()
db := setupDB(t)

// Run multiple analyzers, each on a separate block range, to simulate past Nexus invocations.
// Note: The .Start() call blocks until the analyzer finishes.
p := &mockProcessor{name: "consensus", storage: db}
setupAnalyzer(t, db, p, &config.BlockBasedAnalyzerConfig{From: 3, To: 10}, analyzer.SlowSyncMode).Start(ctx)

p = &mockProcessor{name: "consensus", storage: db}
a := setupAnalyzer(t, db, p, &config.BlockBasedAnalyzerConfig{From: 1, To: 15}, analyzer.SlowSyncMode)
a.Start(ctx)
require.Zero(t, len(p.processedBlocks),
"slow-sync analyzer should refuse to process anything because the already-analyzed range is non-contiguous")

// Patch up the holes with a fast-sync analyzer.
fp := &mockProcessor{name: "consensus", storage: db}
setupAnalyzer(t, db, fp, &config.BlockBasedAnalyzerConfig{From: 1, To: 10, FastSync: &config.FastSyncConfig{To: 10}}, analyzer.FastSyncMode).Start(ctx)
require.Equal(t, fp.processedBlocks, map[uint64]struct{}{1: {}, 2: {}},
"fast-sync analyzer should have processed the missing blocks")

// Try a slow-sync analyzer again
a.Start(ctx)
require.Equal(t, p.processedBlocks, map[uint64]struct{}{11: {}, 12: {}, 13: {}, 14: {}, 15: {}},
"slow-sync analyzer should have processed the missing blocks")
}

0 comments on commit 935c55d

Please sign in to comment.