From 60a8a63e62eb6c720e818f45e4a668378aae38b4 Mon Sep 17 00:00:00 2001 From: Jeremiah Andrews Date: Tue, 26 Jun 2018 13:01:45 -0700 Subject: [PATCH 1/3] base variabls / logic, need tests --- store/iavlstore.go | 31 +++++++++++++++++-------------- store/iavlstore_test.go | 13 +++++++------ store/prefixstore_test.go | 2 +- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/store/iavlstore.go b/store/iavlstore.go index 2911598033..16fb8e4f04 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -15,7 +15,8 @@ import ( const ( defaultIAVLCacheSize = 10000 - defaultIAVLNumHistory = 1<<53 - 1 // DEPRECATED + defaultIAVLNumRecent = 1000 + defaultIAVLStoreEvery = 10000 ) // load the iavl store @@ -25,7 +26,7 @@ func LoadIAVLStore(db dbm.DB, id CommitID) (CommitStore, error) { if err != nil { return nil, err } - store := newIAVLStore(tree, defaultIAVLNumHistory) + store := newIAVLStore(tree, defaultIAVLNumRecent, defaultIAVLStoreEvery) return store, nil } @@ -43,16 +44,17 @@ type iavlStore struct { // How many old versions we hold onto. // A value of 0 means keep all history. - numHistory int64 + numRecent int64 + + storeEvery int64 } // CONTRACT: tree should be fully loaded. -// TODO: use more numHistory's, so the below nolint can be removed -// nolint: unparam -func newIAVLStore(tree *iavl.VersionedTree, numHistory int64) *iavlStore { +func newIAVLStore(tree *iavl.VersionedTree, numRecent int64, storeEvery int64) *iavlStore { st := &iavlStore{ tree: tree, - numHistory: numHistory, + numRecent: numRecent, + storeEvery: storeEvery, } return st } @@ -67,13 +69,14 @@ func (st *iavlStore) Commit() CommitID { panic(err) } - // Release an old version of history - if st.numHistory > 0 && (st.numHistory < st.tree.Version64()) { - toRelease := version - st.numHistory - err := st.tree.DeleteVersion(toRelease) - if err != nil { - // TODO: Handle with #870 - panic(err) + // Release an old version of history, if not a sync waypoint + if st.numRecent < st.tree.Version64() { + toRelease := version - st.numRecent + if toRelease%st.storeEvery != 0 { + err := st.tree.DeleteVersion(toRelease) + if err != nil { + panic(err) + } } } diff --git a/store/iavlstore_test.go b/store/iavlstore_test.go index 50d690d908..adae84f1ab 100644 --- a/store/iavlstore_test.go +++ b/store/iavlstore_test.go @@ -15,7 +15,8 @@ import ( var ( cacheSize = 100 - numHistory int64 = 5 + numRecent int64 = 5 + storeEvery int64 = 3 ) var ( @@ -45,7 +46,7 @@ func newTree(t *testing.T, db dbm.DB) (*iavl.VersionedTree, CommitID) { func TestIAVLStoreGetSetHasDelete(t *testing.T) { db := dbm.NewMemDB() tree, _ := newTree(t, db) - iavlStore := newIAVLStore(tree, numHistory) + iavlStore := newIAVLStore(tree, numRecent, storeEvery) key := "hello" @@ -70,7 +71,7 @@ func TestIAVLStoreGetSetHasDelete(t *testing.T) { func TestIAVLIterator(t *testing.T) { db := dbm.NewMemDB() tree, _ := newTree(t, db) - iavlStore := newIAVLStore(tree, numHistory) + iavlStore := newIAVLStore(tree, numRecent, storeEvery) iter := iavlStore.Iterator([]byte("aloha"), []byte("hellz")) expected := []string{"aloha", "hello"} var i int @@ -143,7 +144,7 @@ func TestIAVLIterator(t *testing.T) { func TestIAVLSubspaceIterator(t *testing.T) { db := dbm.NewMemDB() tree, _ := newTree(t, db) - iavlStore := newIAVLStore(tree, numHistory) + iavlStore := newIAVLStore(tree, numRecent, storeEvery) iavlStore.Set([]byte("test1"), []byte("test1")) iavlStore.Set([]byte("test2"), []byte("test2")) @@ -202,7 +203,7 @@ func TestIAVLSubspaceIterator(t *testing.T) { func TestIAVLReverseSubspaceIterator(t *testing.T) { db := dbm.NewMemDB() tree, _ := newTree(t, db) - iavlStore := newIAVLStore(tree, numHistory) + iavlStore := newIAVLStore(tree, numRecent, storeEvery) iavlStore.Set([]byte("test1"), []byte("test1")) iavlStore.Set([]byte("test2"), []byte("test2")) @@ -261,7 +262,7 @@ func TestIAVLReverseSubspaceIterator(t *testing.T) { func TestIAVLStoreQuery(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewVersionedTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numHistory) + iavlStore := newIAVLStore(tree, numRecent, storeEvery) k1, v1 := []byte("key1"), []byte("val1") k2, v2 := []byte("key2"), []byte("val2") diff --git a/store/prefixstore_test.go b/store/prefixstore_test.go index f57ab20e40..1961bb4bb0 100644 --- a/store/prefixstore_test.go +++ b/store/prefixstore_test.go @@ -66,7 +66,7 @@ func testPrefixStore(t *testing.T, baseStore KVStore, prefix []byte) { func TestIAVLStorePrefix(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewVersionedTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numHistory) + iavlStore := newIAVLStore(tree, numRecent, storeEvery) testPrefixStore(t, iavlStore, []byte("test")) } From 1632b7de1841a38d43a1ea422eaf20a364537cec Mon Sep 17 00:00:00 2001 From: Jeremiah Andrews Date: Tue, 26 Jun 2018 16:26:24 -0700 Subject: [PATCH 2/3] testing pruning --- store/iavlstore.go | 7 ++++++- store/iavlstore_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/store/iavlstore.go b/store/iavlstore.go index 16fb8e4f04..8627879361 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -15,7 +15,7 @@ import ( const ( defaultIAVLCacheSize = 10000 - defaultIAVLNumRecent = 1000 + defaultIAVLNumRecent = 100 defaultIAVLStoreEvery = 10000 ) @@ -94,6 +94,11 @@ func (st *iavlStore) LastCommitID() CommitID { } } +// VersionExists returns whether or not a given version is stored +func (st *iavlStore) VersionExists(version int64) bool { + return st.tree.VersionExists(version) +} + // Implements Store. func (st *iavlStore) GetStoreType() StoreType { return sdk.StoreTypeIAVL diff --git a/store/iavlstore_test.go b/store/iavlstore_test.go index adae84f1ab..51ec31f071 100644 --- a/store/iavlstore_test.go +++ b/store/iavlstore_test.go @@ -259,6 +259,35 @@ func TestIAVLReverseSubspaceIterator(t *testing.T) { require.Equal(t, len(expected), i) } +func nextVersion(iavl *iavlStore) { + key := cmn.RandBytes(12) + value := cmn.RandBytes(50) + iavl.Set(key, value) + iavl.Commit() +} +func TestIAVLPruning(t *testing.T) { + db := dbm.NewMemDB() + tree := iavl.NewVersionedTree(db, cacheSize) + iavlStore := newIAVLStore(tree, numRecent, storeEvery) + nextVersion(iavlStore) + var i, j int64 + for i = 1; i <= 100; i++ { + for j = 1; j <= i; j++ { + if (i-j) < numRecent || j%storeEvery == int64(0) { + assert.True(t, iavlStore.VersionExists(j), + "Missing version %d with latest version %d. Should save last %d and every %d", + j, i, numRecent, storeEvery) + } else { + assert.False(t, iavlStore.VersionExists(j), + "Unpruned version %d with latest version %d. Should prune all but last %d and every %d", + j, i, numRecent, storeEvery) + } + } + nextVersion(iavlStore) + } + +} + func TestIAVLStoreQuery(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewVersionedTree(db, cacheSize) From b3e4faec74558a302a0a9f073c512bc99d4a5447 Mon Sep 17 00:00:00 2001 From: Jeremiah Andrews Date: Mon, 2 Jul 2018 15:17:45 -0700 Subject: [PATCH 3/3] addressing comments --- store/iavlstore.go | 16 ++++++-- store/iavlstore_test.go | 85 ++++++++++++++++++++++++++++++++--------- 2 files changed, 80 insertions(+), 21 deletions(-) diff --git a/store/iavlstore.go b/store/iavlstore.go index 8627879361..26a0c9ea17 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -43,9 +43,16 @@ type iavlStore struct { tree *iavl.VersionedTree // How many old versions we hold onto. - // A value of 0 means keep all history. + // A value of 0 means keep no recent states numRecent int64 + // 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 + // TODO if set to non-default, signal to peers that the node is not suitable as a state sync source storeEvery int64 } @@ -70,9 +77,10 @@ func (st *iavlStore) Commit() CommitID { } // Release an old version of history, if not a sync waypoint - if st.numRecent < st.tree.Version64() { - toRelease := version - st.numRecent - if toRelease%st.storeEvery != 0 { + 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 err != nil { panic(err) diff --git a/store/iavlstore_test.go b/store/iavlstore_test.go index 51ec31f071..f6f236dd8e 100644 --- a/store/iavlstore_test.go +++ b/store/iavlstore_test.go @@ -1,6 +1,7 @@ package store import ( + "fmt" "testing" "github.com/stretchr/testify/require" @@ -260,32 +261,82 @@ func TestIAVLReverseSubspaceIterator(t *testing.T) { } func nextVersion(iavl *iavlStore) { - key := cmn.RandBytes(12) - value := cmn.RandBytes(50) + key := []byte(fmt.Sprintf("Key for tree: %d", iavl.LastCommitID().Version)) + value := []byte(fmt.Sprintf("Value for tree: %d", iavl.LastCommitID().Version)) iavl.Set(key, value) iavl.Commit() } -func TestIAVLPruning(t *testing.T) { +func TestIAVLDefaultPruning(t *testing.T) { + //Expected stored / deleted version numbers for: + //numRecent = 5, storeEvery = 3 + var states = []struct { + stored []int64 + deleted []int64 + }{ + {[]int64{}, []int64{}}, + {[]int64{1}, []int64{}}, + {[]int64{1, 2}, []int64{}}, + {[]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{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, 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, 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}}, + } db := dbm.NewMemDB() tree := iavl.NewVersionedTree(db, cacheSize) iavlStore := newIAVLStore(tree, numRecent, storeEvery) - nextVersion(iavlStore) - var i, j int64 - for i = 1; i <= 100; i++ { - for j = 1; j <= i; j++ { - if (i-j) < numRecent || j%storeEvery == int64(0) { - assert.True(t, iavlStore.VersionExists(j), - "Missing version %d with latest version %d. Should save last %d and every %d", - j, i, numRecent, storeEvery) - } else { - assert.False(t, iavlStore.VersionExists(j), - "Unpruned version %d with latest version %d. Should prune all but last %d and every %d", - j, i, numRecent, storeEvery) - } + 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", + 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", + ver, step, numRecent, storeEvery) } nextVersion(iavlStore) } - +} +func TestIAVLNoPrune(t *testing.T) { + db := dbm.NewMemDB() + tree := iavl.NewVersionedTree(db, cacheSize) + iavlStore := newIAVLStore(tree, numRecent, int64(1)) + nextVersion(iavlStore) + for i := 1; i < 100; i++ { + for j := 1; j <= i; j++ { + require.True(t, iavlStore.VersionExists(int64(j)), + "Missing version %d with latest version %d. Should be storing all versions", + j, i) + } + nextVersion(iavlStore) + } +} +func TestIAVLPruneEverything(t *testing.T) { + db := dbm.NewMemDB() + tree := iavl.NewVersionedTree(db, cacheSize) + iavlStore := newIAVLStore(tree, int64(0), int64(0)) + 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", + j, i) + } + require.True(t, iavlStore.VersionExists(int64(i)), + "Missing current version on step %d, should not prune current state tree", + i) + nextVersion(iavlStore) + } } func TestIAVLStoreQuery(t *testing.T) {