diff options
Diffstat (limited to 'ledger/blockqueue_test.go')
-rw-r--r-- | ledger/blockqueue_test.go | 117 |
1 files changed, 116 insertions, 1 deletions
diff --git a/ledger/blockqueue_test.go b/ledger/blockqueue_test.go index 2d702cfb9..e74fbc0b3 100644 --- a/ledger/blockqueue_test.go +++ b/ledger/blockqueue_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2023 Algorand, Inc. +// Copyright (C) 2019-2024 Algorand, Inc. // This file is part of go-algorand // // go-algorand is free software: you can redistribute it and/or modify @@ -17,9 +17,12 @@ package ledger import ( + "context" + "database/sql" "errors" "fmt" "testing" + "time" "github.com/stretchr/testify/require" @@ -29,10 +32,12 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/ledger/ledgercore" + "github.com/algorand/go-algorand/ledger/store/blockdb" ledgertesting "github.com/algorand/go-algorand/ledger/testing" "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/partitiontest" + "github.com/algorand/go-algorand/util/db" ) func randomBlock(r basics.Round) blockEntry { @@ -128,3 +133,113 @@ func TestGetEncodedBlockCert(t *testing.T) { expectedErr := &ledgercore.ErrNoEntry{} require.True(t, errors.As(err, expectedErr)) } + +// it is not great to use trackers here but at the moment there is no abstraction for the ledger +type uptoTracker struct { + emptyTracker +} + +// committedUpTo in the emptyTracker just stores the committed round. +func (t *uptoTracker) committedUpTo(committedRnd basics.Round) (minRound, lookback basics.Round) { + return 5_000, basics.Round(0) +} + +// TestBlockQueueSyncerDeletion ensures that the block queue syncer deletes no more than maxDeletionBatchSize blocks at time +func TestBlockQueueSyncerDeletion(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + tests := []struct { + name string + expectedEarliest basics.Round + tracker ledgerTracker + }{ + {"max_batch", maxDeletionBatchSize, nil}, // no trackers, max deletion + {"5k_tracker", 5_000, &uptoTracker{}}, // tracker sets minToSave to 5k + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + + const dbMem = true + blockDBs, err := db.OpenPair(t.Name()+".block.sqlite", dbMem) + require.NoError(t, err) + + log := logging.TestingLog(t) + err = blockDBs.Wdb.Atomic(func(ctx context.Context, tx *sql.Tx) error { + return initBlocksDB(tx, log, []bookkeeping.Block{}, false) + }) + require.NoError(t, err) + + // add 15k blocks + const maxBlocks = maxDeletionBatchSize + maxDeletionBatchSize/2 // 15_000 + err = blockDBs.Wdb.Atomic(func(ctx context.Context, tx *sql.Tx) error { + for i := 0; i < maxBlocks; i++ { + err0 := blockdb.BlockPut( + tx, + bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: basics.Round(i)}}, + agreement.Certificate{}) + if err0 != nil { + return err0 + } + } + return nil + }) + require.NoError(t, err) + + var earliest, latest basics.Round + err = blockDBs.Rdb.Atomic(func(ctx context.Context, tx *sql.Tx) error { + var err0 error + earliest, err0 = blockdb.BlockEarliest(tx) + if err0 != nil { + return err0 + } + latest, err0 = blockdb.BlockLatest(tx) + return err0 + }) + require.NoError(t, err) + require.Equal(t, basics.Round(0), earliest) + require.Equal(t, basics.Round(maxBlocks-1), latest) + + // trigger deletion and ensure no more than 10k blocks gone + //make a minimal ledger for blockqueue + + l := &Ledger{ + log: log, + blockDBs: blockDBs, + } + if test.tracker != nil { + l.trackers.trackers = append(l.trackers.trackers, test.tracker) + } + blockq, _ := newBlockQueue(l) + err = blockq.start() + require.NoError(t, err) + + // add a block. Eventually the syncer will called on an empty ledger + // forcing deleting all 15_000 rounds. The deletion scoping should limit it to 10_000 rounds instead + err = blockq.putBlock(bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: maxBlocks}}, agreement.Certificate{}) + require.NoError(t, err) + + require.Eventually(t, func() bool { + var latest basics.Round + err = blockDBs.Rdb.Atomic(func(ctx context.Context, tx *sql.Tx) error { + var err0 error + latest, err0 = blockdb.BlockLatest(tx) + return err0 + }) + require.NoError(t, err) + return latest == maxBlocks + }, 1*time.Second, 10*time.Millisecond) + + blockq.stop() + + err = blockDBs.Rdb.Atomic(func(ctx context.Context, tx *sql.Tx) error { + var err0 error + earliest, err0 = blockdb.BlockEarliest(tx) + return err0 + }) + require.NoError(t, err) + require.Equal(t, test.expectedEarliest, earliest) + }) + } +} |