Skip to content

Commit

Permalink
refactor!: abstractions for snapshot and pruning; snapshot intervals …
Browse files Browse the repository at this point in the history
…eventually pruned; unit tests (#11496)
  • Loading branch information
p0mvn authored Apr 21, 2022
1 parent d4dd444 commit 42f8d45
Show file tree
Hide file tree
Showing 57 changed files with 2,643 additions and 638 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### API Breaking Changes

* [\#11496](https://github.com/cosmos/cosmos-sdk/pull/11496) Refactor abstractions for snapshot and pruning; snapshot intervals eventually pruned; unit tests.
* (types) [\#11689](https://github.com/cosmos/cosmos-sdk/pull/11689) Make `Coins#Sub` and `Coins#SafeSub` consistent with `Coins#Add`.
* (store)[\#11152](https://github.com/cosmos/cosmos-sdk/pull/11152) Remove `keep-every` from pruning options.
* [\#10950](https://github.com/cosmos/cosmos-sdk/pull/10950) Add `envPrefix` parameter to `cmd.Execute`.
Expand Down
42 changes: 6 additions & 36 deletions baseapp/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,9 +338,7 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) {
app.halt()
}

if app.snapshotInterval > 0 && uint64(header.Height)%app.snapshotInterval == 0 {
go app.snapshot(header.Height)
}
go app.snapshotManager.SnapshotIfApplicable(header.Height)

return abci.ResponseCommit{
Data: commitID.Hash,
Expand Down Expand Up @@ -370,36 +368,6 @@ func (app *BaseApp) halt() {
os.Exit(0)
}

// snapshot takes a snapshot of the current state and prunes any old snapshottypes.
func (app *BaseApp) snapshot(height int64) {
if app.snapshotManager == nil {
app.logger.Info("snapshot manager not configured")
return
}

app.logger.Info("creating state snapshot", "height", height)

snapshot, err := app.snapshotManager.Create(uint64(height))
if err != nil {
app.logger.Error("failed to create state snapshot", "height", height, "err", err)
return
}

app.logger.Info("completed state snapshot", "height", height, "format", snapshot.Format)

if app.snapshotKeepRecent > 0 {
app.logger.Debug("pruning state snapshots")

pruned, err := app.snapshotManager.Prune(app.snapshotKeepRecent)
if err != nil {
app.logger.Error("Failed to prune state snapshots", "err", err)
return
}

app.logger.Debug("pruned state snapshots", "pruned", pruned)
}
}

// Query implements the ABCI interface. It delegates to CommitMultiStore if it
// implements Queryable.
func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
Expand Down Expand Up @@ -718,9 +686,11 @@ func (app *BaseApp) GetBlockRetentionHeight(commitHeight int64) int64 {
retentionHeight = commitHeight - cp.Evidence.MaxAgeNumBlocks
}

if app.snapshotInterval > 0 && app.snapshotKeepRecent > 0 {
v := commitHeight - int64((app.snapshotInterval * uint64(app.snapshotKeepRecent)))
retentionHeight = minNonZero(retentionHeight, v)
if app.snapshotManager != nil {
snapshotRetentionHeights := app.snapshotManager.GetSnapshotBlockRetentionHeights()
if snapshotRetentionHeights > 0 {
retentionHeight = minNonZero(retentionHeight, commitHeight-snapshotRetentionHeights)
}
}

v := commitHeight - int64(app.minRetainBlocks)
Expand Down
28 changes: 19 additions & 9 deletions baseapp/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,24 @@ import (

"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmprototypes "github.com/tendermint/tendermint/proto/tendermint/types"
dbm "github.com/tendermint/tm-db"

"github.com/cosmos/cosmos-sdk/baseapp"
pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types"
snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types"
"github.com/cosmos/cosmos-sdk/snapshots"
"github.com/cosmos/cosmos-sdk/testutil"
)

func TestGetBlockRentionHeight(t *testing.T) {
logger := defaultLogger()
db := dbm.NewMemDB()
name := t.Name()

snapshotStore, err := snapshots.NewStore(dbm.NewMemDB(), testutil.GetTempDir(t))
require.NoError(t, err)

testCases := map[string]struct {
bapp *baseapp.BaseApp
maxAgeBlocks int64
Expand All @@ -38,17 +44,18 @@ func TestGetBlockRentionHeight(t *testing.T) {
"pruning iavl snapshot only": {
bapp: baseapp.NewBaseApp(
name, logger, db,
baseapp.SetPruning(pruningtypes.NewPruningOptions(pruningtypes.PruningNothing)),
baseapp.SetMinRetainBlocks(1),
baseapp.SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(10000, 1)),
),
maxAgeBlocks: 0,
commitHeight: 499000,
expected: 498999,
expected: 489000,
},
"pruning state sync snapshot only": {
bapp: baseapp.NewBaseApp(
name, logger, db,
baseapp.SetSnapshotInterval(50000),
baseapp.SetSnapshotKeepRecent(3),
baseapp.SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(50000, 3)),
baseapp.SetMinRetainBlocks(1),
),
maxAgeBlocks: 0,
Expand All @@ -67,8 +74,9 @@ func TestGetBlockRentionHeight(t *testing.T) {
"pruning all conditions": {
bapp: baseapp.NewBaseApp(
name, logger, db,
baseapp.SetPruning(pruningtypes.NewCustomPruningOptions(0, 0)),
baseapp.SetMinRetainBlocks(400000),
baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3),
baseapp.SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(50000, 3)),
),
maxAgeBlocks: 362880,
commitHeight: 499000,
Expand All @@ -77,8 +85,9 @@ func TestGetBlockRentionHeight(t *testing.T) {
"no pruning due to no persisted state": {
bapp: baseapp.NewBaseApp(
name, logger, db,
baseapp.SetPruning(pruningtypes.NewCustomPruningOptions(0, 0)),
baseapp.SetMinRetainBlocks(400000),
baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3),
baseapp.SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(50000, 3)),
),
maxAgeBlocks: 362880,
commitHeight: 10000,
Expand All @@ -87,8 +96,9 @@ func TestGetBlockRentionHeight(t *testing.T) {
"disable pruning": {
bapp: baseapp.NewBaseApp(
name, logger, db,
baseapp.SetPruning(pruningtypes.NewCustomPruningOptions(0, 0)),
baseapp.SetMinRetainBlocks(0),
baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3),
baseapp.SetSnapshot(snapshotStore, snapshottypes.NewSnapshotOptions(50000, 3)),
),
maxAgeBlocks: 362880,
commitHeight: 499000,
Expand Down Expand Up @@ -126,10 +136,10 @@ func TestBaseAppCreateQueryContext(t *testing.T) {
name := t.Name()
app := baseapp.NewBaseApp(name, logger, db)

app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: 1}})
app.BeginBlock(abci.RequestBeginBlock{Header: tmprototypes.Header{Height: 1}})
app.Commit()

app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: 2}})
app.BeginBlock(abci.RequestBeginBlock{Header: tmprototypes.Header{Height: 2}})
app.Commit()

testCases := []struct {
Expand Down
26 changes: 12 additions & 14 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package baseapp

import (
"context"
"errors"
"fmt"

abci "github.com/tendermint/tendermint/abci/types"
Expand Down Expand Up @@ -62,9 +61,7 @@ type BaseApp struct { // nolint: maligned
fauxMerkleMode bool // if true, IAVL MountStores uses MountStoresDB for simulation speed.

// manages snapshots, i.e. dumps of app state at certain intervals
snapshotManager *snapshots.Manager
snapshotInterval uint64 // block interval between state sync snapshots
snapshotKeepRecent uint32 // recent state sync snapshots to keep
snapshotManager *snapshots.Manager

// volatile states:
//
Expand Down Expand Up @@ -252,7 +249,7 @@ func (app *BaseApp) LoadLatestVersion() error {
return fmt.Errorf("failed to load latest version: %w", err)
}

return app.init()
return app.Init()
}

// DefaultStoreLoader will be used by default and loads the latest version
Expand Down Expand Up @@ -284,7 +281,7 @@ func (app *BaseApp) LoadVersion(version int64) error {
return fmt.Errorf("failed to load version %d: %w", version, err)
}

return app.init()
return app.Init()
}

// LastCommitID returns the last CommitID of the multistore.
Expand All @@ -297,7 +294,11 @@ func (app *BaseApp) LastBlockHeight() int64 {
return app.cms.LastCommitID().Version
}

func (app *BaseApp) init() error {
// Init initializes the app. It seals the app, preventing any
// further modifications. In addition, it validates the app against
// the earlier provided settings. Returns an error if validation fails.
// nil otherwise. Panics if the app is already sealed.
func (app *BaseApp) Init() error {
if app.sealed {
panic("cannot call initFromMainStore: baseapp already sealed")
}
Expand All @@ -306,14 +307,11 @@ func (app *BaseApp) init() error {
app.setCheckState(tmproto.Header{})
app.Seal()

// make sure the snapshot interval is a multiple of the pruning KeepEvery interval
if app.snapshotManager != nil && app.snapshotInterval > 0 {
if _, ok := app.cms.(*rootmulti.Store); !ok {
return errors.New("state sync snapshots require a rootmulti store")
}
rms, ok := app.cms.(*rootmulti.Store)
if !ok {
return fmt.Errorf("invalid commit multi-store; expected %T, got: %T", &rootmulti.Store{}, app.cms)
}

return nil
return rms.GetPruning().Validate()
}

func (app *BaseApp) setMinGasPrices(gasPrices sdk.DecCoins) {
Expand Down
Loading

0 comments on commit 42f8d45

Please sign in to comment.