chore(store/v2): refactor the pruning option (#20730)
This commit is contained in:
parent
43a7b99f67
commit
ad52b415f1
@ -114,7 +114,7 @@ func NewSimApp(
|
||||
RootDir: DefaultNodeHome,
|
||||
SSType: 0,
|
||||
SCType: 0,
|
||||
SCPruneOptions: &store.PruneOptions{
|
||||
SCPruningOption: &store.PruningOption{
|
||||
KeepRecent: 0,
|
||||
Interval: 0,
|
||||
},
|
||||
|
||||
129
store/v2/commitment/store_bench_test.go
Normal file
129
store/v2/commitment/store_bench_test.go
Normal file
@ -0,0 +1,129 @@
|
||||
//go:build rocksdb
|
||||
// +build rocksdb
|
||||
|
||||
package commitment_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/core/log"
|
||||
corestore "cosmossdk.io/core/store"
|
||||
"cosmossdk.io/store/v2/commitment"
|
||||
"cosmossdk.io/store/v2/commitment/iavl"
|
||||
dbm "cosmossdk.io/store/v2/db"
|
||||
)
|
||||
|
||||
var (
|
||||
storeKeys = []string{"store1", "store2", "store3"}
|
||||
dbBackends = map[string]func(dataDir string) (corestore.KVStoreWithBatch, error){
|
||||
"rocksdb_opts": func(dataDir string) (corestore.KVStoreWithBatch, error) {
|
||||
return dbm.NewRocksDB("test", dataDir)
|
||||
},
|
||||
"pebbledb_opts": func(dataDir string) (corestore.KVStoreWithBatch, error) {
|
||||
return dbm.NewPebbleDB("test", dataDir)
|
||||
},
|
||||
"goleveldb_opts": func(dataDir string) (corestore.KVStoreWithBatch, error) {
|
||||
return dbm.NewGoLevelDB("test", dataDir, nil)
|
||||
},
|
||||
}
|
||||
rng = rand.New(rand.NewSource(543210))
|
||||
changesets = make([]*corestore.Changeset, 1000)
|
||||
)
|
||||
|
||||
func init() {
|
||||
for i := 0; i < 1000; i++ {
|
||||
cs := corestore.NewChangeset()
|
||||
for _, storeKey := range storeKeys {
|
||||
for j := 0; j < 100; j++ {
|
||||
key := make([]byte, 16)
|
||||
val := make([]byte, 16)
|
||||
|
||||
_, err := rng.Read(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = rng.Read(val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cs.AddKVPair([]byte(storeKey), corestore.KVPair{Key: key, Value: val})
|
||||
}
|
||||
}
|
||||
changesets[i] = cs
|
||||
}
|
||||
}
|
||||
|
||||
func getCommitStore(b *testing.B, db corestore.KVStoreWithBatch) *commitment.CommitStore {
|
||||
b.Helper()
|
||||
multiTrees := make(map[string]commitment.Tree)
|
||||
for _, storeKey := range storeKeys {
|
||||
prefixDB := dbm.NewPrefixDB(db, []byte(storeKey))
|
||||
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, log.NewNopLogger(), iavl.DefaultConfig())
|
||||
}
|
||||
|
||||
sc, err := commitment.NewCommitStore(multiTrees, db, log.NewNopLogger())
|
||||
require.NoError(b, err)
|
||||
|
||||
return sc
|
||||
}
|
||||
|
||||
func BenchmarkCommit(b *testing.B) {
|
||||
for ty, fn := range dbBackends {
|
||||
b.Run(fmt.Sprintf("backend_%s", ty), func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
b.StopTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
db, err := fn(b.TempDir())
|
||||
require.NoError(b, err)
|
||||
sc := getCommitStore(b, db)
|
||||
b.StartTimer()
|
||||
for j, cs := range changesets {
|
||||
require.NoError(b, sc.WriteChangeset(cs))
|
||||
_, err := sc.Commit(uint64(j + 1))
|
||||
require.NoError(b, err)
|
||||
}
|
||||
b.StopTimer()
|
||||
require.NoError(b, db.Close())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetProof(b *testing.B) {
|
||||
for ty, fn := range dbBackends {
|
||||
db, err := fn(b.TempDir())
|
||||
require.NoError(b, err)
|
||||
sc := getCommitStore(b, db)
|
||||
|
||||
b.Run(fmt.Sprintf("backend_%s", ty), func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
b.StopTimer()
|
||||
// commit some changesets
|
||||
for i, cs := range changesets {
|
||||
require.NoError(b, sc.WriteChangeset(cs))
|
||||
_, err = sc.Commit(uint64(i + 1))
|
||||
require.NoError(b, err)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
// non-existing proof
|
||||
p, err := sc.GetProof([]byte(storeKeys[0]), 500, []byte("key-1-1"))
|
||||
require.NoError(b, err)
|
||||
require.NotNil(b, p)
|
||||
// existing proof
|
||||
p, err = sc.GetProof([]byte(storeKeys[1]), 500, changesets[499].Changes[1].StateChanges[1].Key)
|
||||
require.NoError(b, err)
|
||||
require.NotNil(b, p)
|
||||
}
|
||||
})
|
||||
require.NoError(b, db.Close())
|
||||
}
|
||||
}
|
||||
@ -125,10 +125,7 @@ func (s *CommitStoreTestSuite) TestStore_Snapshotter() {
|
||||
|
||||
func (s *CommitStoreTestSuite) TestStore_Pruning() {
|
||||
storeKeys := []string{storeKey1, storeKey2}
|
||||
pruneOpts := &store.PruneOptions{
|
||||
KeepRecent: 10,
|
||||
Interval: 5,
|
||||
}
|
||||
pruneOpts := store.NewPruningOptionWithCustom(10, 5)
|
||||
commitStore, err := s.NewStore(dbm.NewMemDB(), storeKeys, log.NewNopLogger())
|
||||
s.Require().NoError(err)
|
||||
|
||||
|
||||
@ -1,7 +1,24 @@
|
||||
package store
|
||||
|
||||
// PruneOptions defines the pruning configuration.
|
||||
type PruneOptions struct {
|
||||
type PruningStrategy int
|
||||
|
||||
const (
|
||||
// PruningDefault defines a pruning strategy where the last 362880 heights are
|
||||
// kept where to-be pruned heights are pruned at every 10th height.
|
||||
// The last 362880 heights are kept(approximately 3.5 weeks worth of state) assuming the typical
|
||||
// block time is 6s. If these values do not match the applications' requirements, use the "custom" option.
|
||||
PruningDefault PruningStrategy = iota
|
||||
// PruningEverything defines a pruning strategy where all committed heights are
|
||||
// deleted, storing only the current height and last 2 states. To-be pruned heights are
|
||||
// pruned at every 10th height.
|
||||
PruningEverything
|
||||
// PruningNothing defines a pruning strategy where all heights are kept on disk.
|
||||
// This is the only stretegy where KeepEvery=1 is allowed with state-sync snapshots disabled.
|
||||
PruningNothing
|
||||
)
|
||||
|
||||
// PruningOption defines the pruning configuration.
|
||||
type PruningOption struct {
|
||||
// KeepRecent sets the number of recent versions to keep.
|
||||
KeepRecent uint64
|
||||
|
||||
@ -10,19 +27,41 @@ type PruneOptions struct {
|
||||
Interval uint64
|
||||
}
|
||||
|
||||
// DefaultPruneOptions returns the default pruning options.
|
||||
// Interval is set to 0, which means no pruning will be done.
|
||||
func DefaultPruneOptions() *PruneOptions {
|
||||
return &PruneOptions{
|
||||
KeepRecent: 0,
|
||||
Interval: 0,
|
||||
// NewPruningOption returns a new PruningOption instance based on the given pruning strategy.
|
||||
func NewPruningOption(pruningStrategy PruningStrategy) *PruningOption {
|
||||
switch pruningStrategy {
|
||||
case PruningDefault:
|
||||
return &PruningOption{
|
||||
KeepRecent: 362880,
|
||||
Interval: 10,
|
||||
}
|
||||
case PruningEverything:
|
||||
return &PruningOption{
|
||||
KeepRecent: 2,
|
||||
Interval: 10,
|
||||
}
|
||||
case PruningNothing:
|
||||
return &PruningOption{
|
||||
KeepRecent: 0,
|
||||
Interval: 0,
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewPruningOptionWithCustom returns a new PruningOption based on the given parameters.
|
||||
func NewPruningOptionWithCustom(keepRecent, interval uint64) *PruningOption {
|
||||
return &PruningOption{
|
||||
KeepRecent: keepRecent,
|
||||
Interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
// ShouldPrune returns true if the given version should be pruned.
|
||||
// If true, it also returns the version to prune up to.
|
||||
// NOTE: The current version is not pruned.
|
||||
func (opts *PruneOptions) ShouldPrune(version uint64) (bool, uint64) {
|
||||
func (opts *PruningOption) ShouldPrune(version uint64) (bool, uint64) {
|
||||
if opts.Interval == 0 {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
|
||||
The `pruning` package defines the `PruningManager` struct which is responsible for
|
||||
pruning the state storage (SS) and the state commitment (SC) based on the current
|
||||
height of the chain. The `PruneOptions` struct defines the configuration for pruning
|
||||
height of the chain. The `PruningOption` struct defines the configuration for pruning
|
||||
and is passed to the `PruningManager` during initialization.
|
||||
|
||||
## Prune Options
|
||||
|
||||
The `PruneOptions` struct includes the following fields:
|
||||
The `PruningOption` struct includes the following fields:
|
||||
|
||||
* `KeepRecent` (uint64): The number of recent heights to keep in the state.
|
||||
* `Interval` (uint64): The interval of how often to prune the state. 0 means no pruning.
|
||||
|
||||
@ -6,21 +6,21 @@ import "cosmossdk.io/store/v2"
|
||||
type Manager struct {
|
||||
// scPruner is the pruner for the SC.
|
||||
scPruner store.Pruner
|
||||
// scPruningOptions are the pruning options for the SC.
|
||||
scPruningOptions *store.PruneOptions
|
||||
// scPruningOption are the pruning options for the SC.
|
||||
scPruningOption *store.PruningOption
|
||||
// ssPruner is the pruner for the SS.
|
||||
ssPruner store.Pruner
|
||||
// ssPruningOptions are the pruning options for the SS.
|
||||
ssPruningOptions *store.PruneOptions
|
||||
// ssPruningOption are the pruning options for the SS.
|
||||
ssPruningOption *store.PruningOption
|
||||
}
|
||||
|
||||
// NewManager creates a new Pruning Manager.
|
||||
func NewManager(scPruner, ssPruner store.Pruner, scPruningOptions, ssPruningOptions *store.PruneOptions) *Manager {
|
||||
func NewManager(scPruner, ssPruner store.Pruner, scPruningOption, ssPruningOption *store.PruningOption) *Manager {
|
||||
return &Manager{
|
||||
scPruner: scPruner,
|
||||
scPruningOptions: scPruningOptions,
|
||||
ssPruner: ssPruner,
|
||||
ssPruningOptions: ssPruningOptions,
|
||||
scPruner: scPruner,
|
||||
scPruningOption: scPruningOption,
|
||||
ssPruner: ssPruner,
|
||||
ssPruningOption: ssPruningOption,
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,8 +29,8 @@ func NewManager(scPruner, ssPruner store.Pruner, scPruningOptions, ssPruningOpti
|
||||
// NOTE: It can be called outside of the store manually.
|
||||
func (m *Manager) Prune(version uint64) error {
|
||||
// Prune the SC.
|
||||
if m.scPruningOptions != nil {
|
||||
if prune, pruneTo := m.scPruningOptions.ShouldPrune(version); prune {
|
||||
if m.scPruningOption != nil {
|
||||
if prune, pruneTo := m.scPruningOption.ShouldPrune(version); prune {
|
||||
if err := m.scPruner.Prune(pruneTo); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -38,8 +38,8 @@ func (m *Manager) Prune(version uint64) error {
|
||||
}
|
||||
|
||||
// Prune the SS.
|
||||
if m.ssPruningOptions != nil {
|
||||
if prune, pruneTo := m.ssPruningOptions.ShouldPrune(version); prune {
|
||||
if m.ssPruningOption != nil {
|
||||
if prune, pruneTo := m.ssPruningOption.ShouldPrune(version); prune {
|
||||
if err := m.ssPruner.Prune(pruneTo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -48,15 +48,9 @@ func (s *PruningManagerTestSuite) SetupTest() {
|
||||
sqliteDB, err := sqlite.New(s.T().TempDir())
|
||||
s.Require().NoError(err)
|
||||
s.ss = storage.NewStorageStore(sqliteDB, nopLog)
|
||||
scPruneOptions := &store.PruneOptions{
|
||||
KeepRecent: 0,
|
||||
Interval: 1,
|
||||
} // prune all
|
||||
ssPruneOptions := &store.PruneOptions{
|
||||
KeepRecent: 5,
|
||||
Interval: 10,
|
||||
} // prune some
|
||||
s.manager = NewManager(s.sc, s.ss, scPruneOptions, ssPruneOptions)
|
||||
scPruningOption := store.NewPruningOptionWithCustom(0, 1) // prune all
|
||||
ssPruningOption := store.NewPruningOptionWithCustom(5, 10) // prune some
|
||||
s.manager = NewManager(s.sc, s.ss, scPruningOption, ssPruningOption)
|
||||
}
|
||||
|
||||
func (s *PruningManagerTestSuite) TestPrune() {
|
||||
@ -94,7 +88,7 @@ func (s *PruningManagerTestSuite) TestPrune() {
|
||||
s.Require().Eventually(checkSCPrune, 10*time.Second, 1*time.Second)
|
||||
|
||||
// check the storage store
|
||||
_, pruneVersion := s.manager.ssPruningOptions.ShouldPrune(toVersion)
|
||||
_, pruneVersion := s.manager.ssPruningOption.ShouldPrune(toVersion)
|
||||
for version := uint64(1); version <= toVersion; version++ {
|
||||
for _, storeKey := range storeKeys {
|
||||
for i := 0; i < keyCount; i++ {
|
||||
@ -112,50 +106,38 @@ func (s *PruningManagerTestSuite) TestPrune() {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruneOptions(t *testing.T) {
|
||||
func TestPruningOption(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
options *store.PruneOptions
|
||||
options *store.PruningOption
|
||||
version uint64
|
||||
pruning bool
|
||||
pruneVersion uint64
|
||||
}{
|
||||
{
|
||||
name: "no pruning",
|
||||
options: &store.PruneOptions{
|
||||
KeepRecent: 100,
|
||||
Interval: 0,
|
||||
},
|
||||
name: "no pruning",
|
||||
options: store.NewPruningOptionWithCustom(100, 0),
|
||||
version: 100,
|
||||
pruning: false,
|
||||
pruneVersion: 0,
|
||||
},
|
||||
{
|
||||
name: "prune all",
|
||||
options: &store.PruneOptions{
|
||||
KeepRecent: 0,
|
||||
Interval: 1,
|
||||
},
|
||||
name: "prune all",
|
||||
options: store.NewPruningOptionWithCustom(0, 1),
|
||||
version: 19,
|
||||
pruning: true,
|
||||
pruneVersion: 18,
|
||||
},
|
||||
{
|
||||
name: "prune none",
|
||||
options: &store.PruneOptions{
|
||||
KeepRecent: 100,
|
||||
Interval: 10,
|
||||
},
|
||||
name: "prune none",
|
||||
options: store.NewPruningOptionWithCustom(100, 10),
|
||||
version: 19,
|
||||
pruning: false,
|
||||
pruneVersion: 0,
|
||||
},
|
||||
{
|
||||
name: "prune some",
|
||||
options: &store.PruneOptions{
|
||||
KeepRecent: 10,
|
||||
Interval: 50,
|
||||
},
|
||||
name: "prune some",
|
||||
options: store.NewPruningOptionWithCustom(10, 50),
|
||||
version: 100,
|
||||
pruning: true,
|
||||
pruneVersion: 89,
|
||||
|
||||
@ -32,15 +32,15 @@ const (
|
||||
)
|
||||
|
||||
type FactoryOptions struct {
|
||||
Logger log.Logger
|
||||
RootDir string
|
||||
SSType SSType
|
||||
SCType SCType
|
||||
SSPruneOptions *store.PruneOptions
|
||||
SCPruneOptions *store.PruneOptions
|
||||
IavlConfig *iavl.Config
|
||||
StoreKeys []string
|
||||
SCRawDB corestore.KVStoreWithBatch
|
||||
Logger log.Logger
|
||||
RootDir string
|
||||
SSType SSType
|
||||
SCType SCType
|
||||
SSPruningOption *store.PruningOption
|
||||
SCPruningOption *store.PruningOption
|
||||
IavlConfig *iavl.Config
|
||||
StoreKeys []string
|
||||
SCRawDB corestore.KVStoreWithBatch
|
||||
}
|
||||
|
||||
// CreateRootStore is a convenience function to create a root store based on the
|
||||
@ -101,7 +101,7 @@ func CreateRootStore(opts *FactoryOptions) (store.RootStore, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pm := pruning.NewManager(sc, ss, opts.SCPruneOptions, opts.SSPruneOptions)
|
||||
pm := pruning.NewManager(sc, ss, opts.SCPruningOption, opts.SSPruningOption)
|
||||
|
||||
return New(opts.Logger, ss, sc, pm, nil, nil)
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ Iterate/backend_rocksdb_versiondb_opts-10 778ms ± 0%
|
||||
## Pruning
|
||||
|
||||
Pruning is an implementation and responsibility of the underlying SS backend.
|
||||
Specifically, the `StorageStore` accepts `store.PruneOptions` which defines the
|
||||
Specifically, the `StorageStore` accepts `store.PruningOption` which defines the
|
||||
pruning configuration. During `ApplyChangeset`, the `StorageStore` will check if
|
||||
pruning should occur based on the current height being committed. If so, it will
|
||||
delegate a `Prune` call on the underlying SS backend, which can be defined specific
|
||||
|
||||
Loading…
Reference in New Issue
Block a user