summaryrefslogtreecommitdiff
path: root/ledger/blockqueue_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'ledger/blockqueue_test.go')
-rw-r--r--ledger/blockqueue_test.go117
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)
+ })
+ }
+}