Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor IAVL Pruning #5538

Merged
merged 14 commits into from
Jan 22, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ that allows for arbitrary vesting periods.

### Improvements

* (iavl) [\#5538](https://github.com/cosmos/cosmos-sdk/pull/5538) Remove manual IAVL pruning in favor of IAVL's internal pruning strategy.
* (server) [\#4215](https://github.com/cosmos/cosmos-sdk/issues/4215) The `--pruning` flag
has been moved to the configuration file, to allow easier node configuration.
* (cli) [\#5116](https://github.com/cosmos/cosmos-sdk/issues/5116) The `CLIContext` now supports multiple verifiers
Expand Down
4 changes: 2 additions & 2 deletions baseapp/baseapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func useFileUpgradeLoader(upgradeInfoPath string) func(*BaseApp) {

func initStore(t *testing.T, db dbm.DB, storeKey string, k, v []byte) {
rs := rootmulti.NewStore(db)
rs.SetPruning(store.PruneSyncable)
rs.SetPruning(store.PruneNothing)
key := sdk.NewKVStoreKey(storeKey)
rs.MountStoreWithDB(key, store.StoreTypeIAVL, nil)
err := rs.LoadLatestVersion()
Expand Down Expand Up @@ -244,7 +244,7 @@ func TestSetLoader(t *testing.T) {
initStore(t, db, tc.origStoreKey, k, v)

// load the app with the existing db
opts := []func(*BaseApp){SetPruning(store.PruneSyncable)}
opts := []func(*BaseApp){SetPruning(store.PruneNothing)}
if tc.setLoader != nil {
opts = append(opts, tc.setLoader)
}
Expand Down
6 changes: 3 additions & 3 deletions store/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestGetOrSetStoreCache(t *testing.T) {
sKey := types.NewKVStoreKey("test")
tree, err := iavl.NewMutableTree(db, 100)
require.NoError(t, err)
store := iavlstore.UnsafeNewStore(tree, 10, 10)
store := iavlstore.UnsafeNewStore(tree)
store2 := mngr.GetStoreCache(sKey, store)

require.NotNil(t, store2)
Expand All @@ -34,7 +34,7 @@ func TestUnwrap(t *testing.T) {
sKey := types.NewKVStoreKey("test")
tree, err := iavl.NewMutableTree(db, 100)
require.NoError(t, err)
store := iavlstore.UnsafeNewStore(tree, 10, 10)
store := iavlstore.UnsafeNewStore(tree)
_ = mngr.GetStoreCache(sKey, store)

require.Equal(t, store, mngr.Unwrap(sKey))
Expand All @@ -48,7 +48,7 @@ func TestStoreCache(t *testing.T) {
sKey := types.NewKVStoreKey("test")
tree, err := iavl.NewMutableTree(db, 100)
require.NoError(t, err)
store := iavlstore.UnsafeNewStore(tree, 10, 10)
store := iavlstore.UnsafeNewStore(tree)
kvStore := mngr.GetStoreCache(sKey, store)

for i := uint(0); i < cache.DefaultCommitKVStoreCacheSize*2; i++ {
Expand Down
62 changes: 16 additions & 46 deletions store/iavl/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"io"
"sync"

"github.com/pkg/errors"
"github.com/tendermint/iavl"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/merkle"
Expand All @@ -31,25 +30,18 @@ var (
// Store Implements types.KVStore and CommitKVStore.
type Store struct {
tree Tree

// How many old versions we hold onto.
// A value of 0 means keep no recent states.
numRecent int64

// This is the distance between state-sync waypoint states to be stored.
// See https://github.com/tendermint/tendermint/issues/828
// A value of 1 means store every state.
// A value of 0 means store no waypoints. (node cannot assist in state-sync)
// By default this value should be set the same across all nodes,
// so that nodes can know the waypoints their peers store.
storeEvery int64
}

// LoadStore returns an IAVL Store as a CommitKVStore. Internally it will load the
// store's version (id) from the provided DB. An error is returned if the version
// fails to load.
func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions, lazyLoading bool) (types.CommitKVStore, error) {
tree, err := iavl.NewMutableTree(db, defaultIAVLCacheSize)
tree, err := iavl.NewMutableTreeWithOpts(
db,
dbm.NewMemDB(),
defaultIAVLCacheSize,
iavl.PruningOptions(pruning.KeepEvery(), pruning.KeepRecent()),
)
if err != nil {
return nil, err
}
Expand All @@ -64,21 +56,15 @@ func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions, lazyL
return nil, err
}

iavl := UnsafeNewStore(tree, int64(0), int64(0))
iavl.SetPruning(pruning)

return iavl, nil
return &Store{tree: tree}, nil
}

// UnsafeNewStore returns a reference to a new IAVL Store.
// UnsafeNewStore returns a reference to a new IAVL Store with a given mutable
// IAVL tree reference.
//
// CONTRACT: The IAVL tree should be fully loaded.
func UnsafeNewStore(tree *iavl.MutableTree, numRecent int64, storeEvery int64) *Store {
return &Store{
tree: tree,
numRecent: numRecent,
storeEvery: storeEvery,
}
func UnsafeNewStore(tree *iavl.MutableTree) *Store {
return &Store{tree: tree}
}

// GetImmutable returns a reference to a new store backed by an immutable IAVL
Expand All @@ -96,11 +82,7 @@ func (st *Store) GetImmutable(version int64) (*Store, error) {
return nil, err
}

return &Store{
tree: &immutableTree{iTree},
numRecent: 0,
storeEvery: 0,
}, nil
return &Store{tree: &immutableTree{iTree}}, nil
}

// Implements Committer.
Expand All @@ -112,18 +94,6 @@ func (st *Store) Commit() types.CommitID {
panic(err)
}

// Release an old version of history, if not a sync waypoint.
previous := version - 1
if st.numRecent < previous {
toRelease := previous - st.numRecent
if st.storeEvery == 0 || toRelease%st.storeEvery != 0 {
err := st.tree.DeleteVersion(toRelease)
if errCause := errors.Cause(err); errCause != nil && errCause != iavl.ErrVersionDoesNotExist {
panic(err)
}
}
}

return types.CommitID{
Version: version,
Hash: hash,
Expand All @@ -138,10 +108,10 @@ func (st *Store) LastCommitID() types.CommitID {
}
}

// Implements Committer.
func (st *Store) SetPruning(opt types.PruningOptions) {
st.numRecent = opt.KeepRecent()
st.storeEvery = opt.KeepEvery()
// SetPruning panics as pruning options should be provided at initialization
// since IAVl accepts pruning options directly.
func (st *Store) SetPruning(_ types.PruningOptions) {
panic("cannot set pruning options on an initialized IAVL store")
}

// VersionExists returns whether or not a given version is stored.
Expand Down
86 changes: 42 additions & 44 deletions store/iavl/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,8 @@ import (
)

var (
cacheSize = 100
numRecent int64 = 5
storeEvery int64 = 3
)

var (
treeData = map[string]string{
cacheSize = 100
treeData = map[string]string{
"hello": "goodbye",
"aloha": "shalom",
}
Expand All @@ -30,7 +25,6 @@ var (
func randBytes(numBytes int) []byte {
b := make([]byte, numBytes)
_, _ = crand.Read(b)

return b
}

Expand Down Expand Up @@ -58,7 +52,7 @@ func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, types.CommitID) {
func TestGetImmutable(t *testing.T) {
db := dbm.NewMemDB()
tree, cID := newAlohaTree(t, db)
store := UnsafeNewStore(tree, 10, 10)
store := UnsafeNewStore(tree)

require.True(t, tree.Set([]byte("hello"), []byte("adios")))
hash, ver, err := tree.SaveVersion()
Expand Down Expand Up @@ -88,7 +82,7 @@ func TestGetImmutable(t *testing.T) {
func TestTestGetImmutableIterator(t *testing.T) {
db := dbm.NewMemDB()
tree, cID := newAlohaTree(t, db)
store := UnsafeNewStore(tree, 10, 10)
store := UnsafeNewStore(tree)

newStore, err := store.GetImmutable(cID.Version)
require.NoError(t, err)
Expand All @@ -111,7 +105,7 @@ func TestTestGetImmutableIterator(t *testing.T) {
func TestIAVLStoreGetSetHasDelete(t *testing.T) {
db := dbm.NewMemDB()
tree, _ := newAlohaTree(t, db)
iavlStore := UnsafeNewStore(tree, numRecent, storeEvery)
iavlStore := UnsafeNewStore(tree)

key := "hello"

Expand All @@ -136,14 +130,14 @@ func TestIAVLStoreGetSetHasDelete(t *testing.T) {
func TestIAVLStoreNoNilSet(t *testing.T) {
db := dbm.NewMemDB()
tree, _ := newAlohaTree(t, db)
iavlStore := UnsafeNewStore(tree, numRecent, storeEvery)
iavlStore := UnsafeNewStore(tree)
require.Panics(t, func() { iavlStore.Set([]byte("key"), nil) }, "setting a nil value should panic")
}

func TestIAVLIterator(t *testing.T) {
db := dbm.NewMemDB()
tree, _ := newAlohaTree(t, db)
iavlStore := UnsafeNewStore(tree, numRecent, storeEvery)
iavlStore := UnsafeNewStore(tree)
iter := iavlStore.Iterator([]byte("aloha"), []byte("hellz"))
expected := []string{"aloha", "hello"}
var i int
Expand Down Expand Up @@ -219,7 +213,7 @@ func TestIAVLReverseIterator(t *testing.T) {
tree, err := iavl.NewMutableTree(db, cacheSize)
require.NoError(t, err)

iavlStore := UnsafeNewStore(tree, numRecent, storeEvery)
iavlStore := UnsafeNewStore(tree)

iavlStore.Set([]byte{0x00}, []byte("0"))
iavlStore.Set([]byte{0x00, 0x00}, []byte("0 0"))
Expand Down Expand Up @@ -252,7 +246,7 @@ func TestIAVLPrefixIterator(t *testing.T) {
tree, err := iavl.NewMutableTree(db, cacheSize)
require.NoError(t, err)

iavlStore := UnsafeNewStore(tree, numRecent, storeEvery)
iavlStore := UnsafeNewStore(tree)

iavlStore.Set([]byte("test1"), []byte("test1"))
iavlStore.Set([]byte("test2"), []byte("test2"))
Expand Down Expand Up @@ -316,7 +310,7 @@ func TestIAVLReversePrefixIterator(t *testing.T) {
tree, err := iavl.NewMutableTree(db, cacheSize)
require.NoError(t, err)

iavlStore := UnsafeNewStore(tree, numRecent, storeEvery)
iavlStore := UnsafeNewStore(tree)

iavlStore.Set([]byte("test1"), []byte("test1"))
iavlStore.Set([]byte("test2"), []byte("test2"))
Expand Down Expand Up @@ -389,16 +383,16 @@ func TestIAVLDefaultPruning(t *testing.T) {
{[]int64{1, 2, 3}, []int64{}},
{[]int64{1, 2, 3, 4}, []int64{}},
{[]int64{1, 2, 3, 4, 5}, []int64{}},
{[]int64{1, 2, 3, 4, 5, 6}, []int64{}},
{[]int64{2, 3, 4, 5, 6, 7}, []int64{1}},
{[]int64{2, 3, 4, 5, 6}, []int64{1}},
{[]int64{3, 4, 5, 6, 7}, []int64{1, 2}},
{[]int64{3, 4, 5, 6, 7, 8}, []int64{1, 2}},
{[]int64{3, 4, 5, 6, 7, 8, 9}, []int64{1, 2}},
{[]int64{3, 5, 6, 7, 8, 9, 10}, []int64{1, 2, 4}},
{[]int64{3, 5, 6, 7, 8, 9}, []int64{1, 2, 4}},
{[]int64{3, 6, 7, 8, 9, 10}, []int64{1, 2, 4, 5}},
{[]int64{3, 6, 7, 8, 9, 10, 11}, []int64{1, 2, 4, 5}},
{[]int64{3, 6, 7, 8, 9, 10, 11, 12}, []int64{1, 2, 4, 5}},
{[]int64{3, 6, 8, 9, 10, 11, 12, 13}, []int64{1, 2, 4, 5, 7}},
{[]int64{3, 6, 8, 9, 10, 11, 12}, []int64{1, 2, 4, 5, 7}},
{[]int64{3, 6, 9, 10, 11, 12, 13}, []int64{1, 2, 4, 5, 7, 8}},
{[]int64{3, 6, 9, 10, 11, 12, 13, 14}, []int64{1, 2, 4, 5, 7, 8}},
{[]int64{3, 6, 9, 10, 11, 12, 13, 14, 15}, []int64{1, 2, 4, 5, 7, 8}},
{[]int64{3, 6, 9, 11, 12, 13, 14, 15}, []int64{1, 2, 4, 5, 7, 8, 10}},
}
testPruning(t, int64(5), int64(3), states)
}
Expand All @@ -411,18 +405,18 @@ func TestIAVLAlternativePruning(t *testing.T) {
{[]int64{1}, []int64{}},
{[]int64{1, 2}, []int64{}},
{[]int64{1, 2, 3}, []int64{}},
{[]int64{1, 2, 3, 4}, []int64{}},
{[]int64{2, 3, 4, 5}, []int64{1}},
{[]int64{3, 4, 5, 6}, []int64{1, 2}},
{[]int64{4, 5, 6, 7}, []int64{1, 2, 3}},
{[]int64{2, 3, 4}, []int64{1}},
{[]int64{3, 4, 5}, []int64{1, 2}},
{[]int64{4, 5, 6}, []int64{1, 2, 3}},
{[]int64{5, 6, 7}, []int64{1, 2, 3, 4}},
{[]int64{5, 6, 7, 8}, []int64{1, 2, 3, 4}},
{[]int64{5, 6, 7, 8, 9}, []int64{1, 2, 3, 4}},
{[]int64{5, 7, 8, 9, 10}, []int64{1, 2, 3, 4, 6}},
{[]int64{5, 8, 9, 10, 11}, []int64{1, 2, 3, 4, 6, 7}},
{[]int64{5, 9, 10, 11, 12}, []int64{1, 2, 3, 4, 6, 7, 8}},
{[]int64{5, 7, 8, 9}, []int64{1, 2, 3, 4, 6}},
{[]int64{5, 8, 9, 10}, []int64{1, 2, 3, 4, 6, 7}},
{[]int64{5, 9, 10, 11}, []int64{1, 2, 3, 4, 6, 7, 8}},
{[]int64{5, 10, 11, 12}, []int64{1, 2, 3, 4, 6, 7, 8, 9}},
{[]int64{5, 10, 11, 12, 13}, []int64{1, 2, 3, 4, 6, 7, 8, 9}},
{[]int64{5, 10, 11, 12, 13, 14}, []int64{1, 2, 3, 4, 6, 7, 8, 9}},
{[]int64{5, 10, 12, 13, 14, 15}, []int64{1, 2, 3, 4, 6, 7, 8, 9, 11}},
{[]int64{5, 10, 12, 13, 14}, []int64{1, 2, 3, 4, 6, 7, 8, 9, 11}},
{[]int64{5, 10, 13, 14, 15}, []int64{1, 2, 3, 4, 6, 7, 8, 9, 11, 12}},
}
testPruning(t, int64(3), int64(5), states)
}
Expand All @@ -434,21 +428,23 @@ type pruneState struct {

func testPruning(t *testing.T, numRecent int64, storeEvery int64, states []pruneState) {
db := dbm.NewMemDB()
tree, err := iavl.NewMutableTree(db, cacheSize)
iavlOpts := iavl.PruningOptions(storeEvery, numRecent)

tree, err := iavl.NewMutableTreeWithOpts(db, dbm.NewMemDB(), cacheSize, iavlOpts)
require.NoError(t, err)

iavlStore := UnsafeNewStore(tree, numRecent, storeEvery)
iavlStore := UnsafeNewStore(tree)

for step, state := range states {
for _, ver := range state.stored {
require.True(t, iavlStore.VersionExists(ver),
"Missing version %d with latest version %d. Should save last %d and every %d",
"missing version %d with latest version %d; should save last %d and every %d",
ver, step, numRecent, storeEvery)
}

for _, ver := range state.deleted {
require.False(t, iavlStore.VersionExists(ver),
"Unpruned version %d with latest version %d. Should prune all but last %d and every %d",
"not pruned version %d with latest version %d; should prune all but last %d and every %d",
ver, step, numRecent, storeEvery)
}

Expand All @@ -461,7 +457,7 @@ func TestIAVLNoPrune(t *testing.T) {
tree, err := iavl.NewMutableTree(db, cacheSize)
require.NoError(t, err)

iavlStore := UnsafeNewStore(tree, numRecent, int64(1))
iavlStore := UnsafeNewStore(tree)
nextVersion(iavlStore)

for i := 1; i < 100; i++ {
Expand All @@ -477,21 +473,23 @@ func TestIAVLNoPrune(t *testing.T) {

func TestIAVLPruneEverything(t *testing.T) {
db := dbm.NewMemDB()
tree, err := iavl.NewMutableTree(db, cacheSize)
iavlOpts := iavl.PruningOptions(0, 1) // only store latest version in memory

tree, err := iavl.NewMutableTreeWithOpts(db, dbm.NewMemDB(), cacheSize, iavlOpts)
require.NoError(t, err)

iavlStore := UnsafeNewStore(tree, int64(0), int64(0))
iavlStore := UnsafeNewStore(tree)
nextVersion(iavlStore)

for i := 1; i < 100; i++ {
for j := 1; j < i; j++ {
require.False(t, iavlStore.VersionExists(int64(j)),
"Unpruned version %d with latest version %d. Should prune all old versions",
"not pruned version %d with latest version %d; should prune all old versions",
j, i)
}

require.True(t, iavlStore.VersionExists(int64(i)),
"Missing current version on step %d, should not prune current state tree",
"missing current version on step %d; should not prune current state tree",
i)

nextVersion(iavlStore)
Expand All @@ -503,7 +501,7 @@ func TestIAVLStoreQuery(t *testing.T) {
tree, err := iavl.NewMutableTree(db, cacheSize)
require.NoError(t, err)

iavlStore := UnsafeNewStore(tree, numRecent, storeEvery)
iavlStore := UnsafeNewStore(tree)

k1, v1 := []byte("key1"), []byte("val1")
k2, v2 := []byte("key2"), []byte("val2")
Expand Down Expand Up @@ -602,7 +600,7 @@ func BenchmarkIAVLIteratorNext(b *testing.B) {
tree.Set(key, value)
}

iavlStore := UnsafeNewStore(tree, numRecent, storeEvery)
iavlStore := UnsafeNewStore(tree)
iterators := make([]types.Iterator, b.N/treeSize)

for i := 0; i < len(iterators); i++ {
Expand Down
Loading