refactor(store/v2): Migrate some v1 tests for rootmultistore (#20368)

This commit is contained in:
son trinh
2024-06-24 19:53:02 +00:00
committed by GitHub
parent 722ac39873
commit 5762b0b817
+431
View File
@@ -3,6 +3,7 @@ package root
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/suite"
@@ -13,6 +14,7 @@ import (
"cosmossdk.io/store/v2/commitment"
"cosmossdk.io/store/v2/commitment/iavl"
dbm "cosmossdk.io/store/v2/db"
"cosmossdk.io/store/v2/proof"
"cosmossdk.io/store/v2/pruning"
"cosmossdk.io/store/v2/storage"
"cosmossdk.io/store/v2/storage/sqlite"
@@ -24,6 +26,8 @@ const (
testStoreKey3 = "test_store_key3"
)
var testStoreKeys = []string{testStoreKey, testStoreKey2, testStoreKey3}
var (
testStoreKeyBytes = []byte(testStoreKey)
testStoreKey2Bytes = []byte(testStoreKey2)
@@ -60,6 +64,40 @@ func (s *RootStoreTestSuite) SetupTest() {
s.rootStore = rs
}
func (s *RootStoreTestSuite) newStoreWithPruneConfig(config *store.PruneOptions) {
noopLog := log.NewNopLogger()
sqliteDB, err := sqlite.New(s.T().TempDir())
s.Require().NoError(err)
ss := storage.NewStorageStore(sqliteDB, noopLog)
mdb := dbm.NewMemDB()
multiTrees := make(map[string]commitment.Tree)
for _, storeKey := range testStoreKeys {
prefixDB := dbm.NewPrefixDB(mdb, []byte(storeKey))
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig())
}
sc, err := commitment.NewCommitStore(multiTrees, dbm.NewMemDB(), noopLog)
s.Require().NoError(err)
pm := pruning.NewManager(sc, ss, config, config)
rs, err := New(noopLog, ss, sc, pm, nil, nil)
s.Require().NoError(err)
s.rootStore = rs
}
func (s *RootStoreTestSuite) newStoreWithBackendMount(ss store.VersionedDatabase, sc store.Committer, pm *pruning.Manager) {
noopLog := log.NewNopLogger()
rs, err := New(noopLog, ss, sc, pm, nil, nil)
s.Require().NoError(err)
s.rootStore = rs
}
func (s *RootStoreTestSuite) TearDownTest() {
err := s.rootStore.Close()
s.Require().NoError(err)
@@ -309,3 +347,396 @@ func (s *RootStoreTestSuite) TestStateAt() {
}
}
}
func (s *RootStoreTestSuite) TestPrune() {
// perform changes
cs := corestore.NewChangeset()
for i := 0; i < 10; i++ {
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099
cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false)
}
testCases := []struct {
name string
numVersions int64
po store.PruneOptions
deleted []uint64
saved []uint64
}{
{"prune nothing", 10, *store.DefaultPruneOptions(), nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
{"prune everything", 12, store.PruneOptions{
KeepRecent: 1,
Interval: 10,
}, []uint64{1, 2, 3, 4, 5, 6, 7, 8}, []uint64{9, 10, 11, 12}},
{"prune some; no batch", 10, store.PruneOptions{
KeepRecent: 2,
Interval: 1,
}, []uint64{1, 2, 3, 4, 6, 5, 7}, []uint64{8, 9, 10}},
{"prune some; small batch", 10, store.PruneOptions{
KeepRecent: 2,
Interval: 3,
}, []uint64{1, 2, 3, 4, 5, 6}, []uint64{7, 8, 9, 10}},
{"prune some; large batch", 10, store.PruneOptions{
KeepRecent: 2,
Interval: 11,
}, nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
}
for _, tc := range testCases {
tc := tc
s.newStoreWithPruneConfig(&tc.po)
// write keys over multiple versions
for i := int64(0); i < tc.numVersions; i++ {
// execute Commit
cHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(cHash)
}
for _, v := range tc.saved {
ro, err := s.rootStore.StateAt(v)
s.Require().NoError(err, "expected no error when loading height %d at test %s", v, tc.name)
for i := 0; i < 10; i++ {
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099
reader, err := ro.GetReader(testStoreKeyBytes)
s.Require().NoError(err)
result, err := reader.Get([]byte(key))
s.Require().NoError(err)
s.Require().Equal([]byte(val), result, "value should be equal for test: %s", tc.name)
}
}
for _, v := range tc.deleted {
var err error
checkErr := func() bool {
if _, err = s.rootStore.StateAt(v); err != nil {
return true
}
return false
}
// wait for async pruning process to finish
s.Require().Eventually(checkErr, 2*time.Second, 100*time.Millisecond)
s.Require().Error(err, "expected error when loading height %d at test %s", v, tc.name)
}
}
}
func (s *RootStoreTestSuite) TestMultiStore_Pruning_SameHeightsTwice() {
// perform changes
cs := corestore.NewChangeset()
cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false)
const (
numVersions uint64 = 10
keepRecent uint64 = 1
interval uint64 = 10
)
s.newStoreWithPruneConfig(&store.PruneOptions{
KeepRecent: keepRecent,
Interval: interval,
})
s.Require().NoError(s.rootStore.LoadLatestVersion())
for i := uint64(0); i < numVersions; i++ {
// execute Commit
cHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(cHash)
}
latestVer, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(numVersions, latestVer)
for v := uint64(1); v < numVersions-keepRecent; v++ {
var err error
checkErr := func() bool {
if err = s.rootStore.LoadVersion(v); err != nil {
return true
}
return false
}
// wait for async pruning process to finish
s.Require().Eventually(checkErr, 2*time.Second, 100*time.Millisecond, "expected no error when loading height: %d", v)
}
for v := (numVersions - keepRecent); v < numVersions; v++ {
err := s.rootStore.LoadVersion(v)
s.Require().NoError(err, "expected no error when loading height: %d", v)
}
// Get latest
err = s.rootStore.LoadVersion(numVersions - 1)
s.Require().NoError(err)
// Test pruning the same heights again
_, err = s.rootStore.Commit(cs)
s.Require().NoError(err)
// Ensure that can commit one more height with no panic
_, err = s.rootStore.Commit(cs)
s.Require().NoError(err)
}
func (s *RootStoreTestSuite) TestMultiStore_PruningRestart() {
// perform changes
cs := corestore.NewChangeset()
cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false)
pruneOpt := &store.PruneOptions{
KeepRecent: 2,
Interval: 11,
}
noopLog := log.NewNopLogger()
mdb1 := dbm.NewMemDB()
mdb2 := dbm.NewMemDB()
sqliteDB, err := sqlite.New(s.T().TempDir())
s.Require().NoError(err)
ss := storage.NewStorageStore(sqliteDB, noopLog)
tree := iavl.NewIavlTree(mdb1, noopLog, iavl.DefaultConfig())
sc, err := commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree}, mdb2, noopLog)
s.Require().NoError(err)
pm := pruning.NewManager(sc, ss, pruneOpt, pruneOpt)
s.newStoreWithBackendMount(ss, sc, pm)
s.Require().NoError(s.rootStore.LoadLatestVersion())
// Commit enough to build up heights to prune, where on the next block we should
// batch delete.
for i := uint64(0); i < 10; i++ {
// execute Commit
cHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(cHash)
}
latestVer, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
ok, actualHeightToPrune := pruneOpt.ShouldPrune(latestVer)
s.Require().False(ok)
s.Require().Equal(uint64(0), actualHeightToPrune)
// "restart"
sqliteDB, err = sqlite.New(s.T().TempDir())
s.Require().NoError(err)
ss = storage.NewStorageStore(sqliteDB, noopLog)
tree = iavl.NewIavlTree(mdb1, noopLog, iavl.DefaultConfig())
sc, err = commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree}, mdb2, noopLog)
s.Require().NoError(err)
pm = pruning.NewManager(sc, ss, pruneOpt, pruneOpt)
s.newStoreWithBackendMount(ss, sc, pm)
err = s.rootStore.LoadLatestVersion()
s.Require().NoError(err)
latestVer, err = s.rootStore.GetLatestVersion()
s.Require().NoError(err)
ok, actualHeightToPrune = pruneOpt.ShouldPrune(latestVer)
s.Require().False(ok)
s.Require().Equal(uint64(0), actualHeightToPrune)
// commit one more block and ensure the heights have been pruned
// execute Commit
cHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(cHash)
latestVer, err = s.rootStore.GetLatestVersion()
s.Require().NoError(err)
ok, actualHeightToPrune = pruneOpt.ShouldPrune(latestVer)
s.Require().True(ok)
s.Require().Equal(uint64(8), actualHeightToPrune)
for v := uint64(1); v <= actualHeightToPrune; v++ {
checkErr := func() bool {
if err = s.rootStore.LoadVersion(v); err != nil {
return true
}
return false
}
// wait for async pruning process to finish
s.Require().Eventually(checkErr, 2*time.Second, 100*time.Millisecond, "expected error when loading height: %d", v)
}
}
func (s *RootStoreTestSuite) TestMultiStoreRestart() {
noopLog := log.NewNopLogger()
sqliteDB, err := sqlite.New(s.T().TempDir())
s.Require().NoError(err)
ss := storage.NewStorageStore(sqliteDB, noopLog)
mdb1 := dbm.NewMemDB()
mdb2 := dbm.NewMemDB()
multiTrees := make(map[string]commitment.Tree)
for _, storeKey := range testStoreKeys {
prefixDB := dbm.NewPrefixDB(mdb1, []byte(storeKey))
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig())
}
sc, err := commitment.NewCommitStore(multiTrees, mdb2, noopLog)
s.Require().NoError(err)
pm := pruning.NewManager(sc, ss, nil, nil)
s.newStoreWithBackendMount(ss, sc, pm)
s.Require().NoError(s.rootStore.LoadLatestVersion())
// perform changes
for i := 1; i < 3; i++ {
cs := corestore.NewChangeset()
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d_%03d", i, 1) // val000_1, val001_1, ..., val099_1
cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false)
key = fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val = fmt.Sprintf("val%03d_%03d", i, 2) // val000_1, val001_1, ..., val099_1
cs.Add(testStoreKey2Bytes, []byte(key), []byte(val), false)
key = fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
val = fmt.Sprintf("val%03d_%03d", i, 3) // val000_1, val001_1, ..., val099_1
cs.Add(testStoreKey3Bytes, []byte(key), []byte(val), false)
// execute Commit
cHash, err := s.rootStore.Commit(cs)
s.Require().NoError(err)
s.Require().NotNil(cHash)
latestVer, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(i), latestVer)
}
// more changes
cs1 := corestore.NewChangeset()
key := fmt.Sprintf("key%03d", 3) // key000, key001, ..., key099
val := fmt.Sprintf("val%03d_%03d", 3, 1) // val000_1, val001_1, ..., val099_1
cs1.Add(testStoreKeyBytes, []byte(key), []byte(val), false)
key = fmt.Sprintf("key%03d", 3) // key000, key001, ..., key099
val = fmt.Sprintf("val%03d_%03d", 3, 2) // val000_1, val001_1, ..., val099_1
cs1.Add(testStoreKey2Bytes, []byte(key), []byte(val), false)
// execute Commit
cHash, err := s.rootStore.Commit(cs1)
s.Require().NoError(err)
s.Require().NotNil(cHash)
latestVer, err := s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(3), latestVer)
cs2 := corestore.NewChangeset()
key = fmt.Sprintf("key%03d", 4) // key000, key001, ..., key099
val = fmt.Sprintf("val%03d_%03d", 4, 3) // val000_1, val001_1, ..., val099_1
cs2.Add(testStoreKey3Bytes, []byte(key), []byte(val), false)
// execute Commit
cHash, err = s.rootStore.Commit(cs2)
s.Require().NoError(err)
s.Require().NotNil(cHash)
latestVer, err = s.rootStore.GetLatestVersion()
s.Require().NoError(err)
s.Require().Equal(uint64(4), latestVer)
_, ro1, err := s.rootStore.StateLatest()
s.Require().Nil(err)
reader1, err := ro1.GetReader(testStoreKeyBytes)
s.Require().NoError(err)
result1, err := reader1.Get([]byte(fmt.Sprintf("key%03d", 3)))
s.Require().NoError(err)
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 3, 1)), result1, "value should be equal")
// "restart"
multiTrees = make(map[string]commitment.Tree)
for _, storeKey := range testStoreKeys {
prefixDB := dbm.NewPrefixDB(mdb1, []byte(storeKey))
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig())
}
sc, err = commitment.NewCommitStore(multiTrees, mdb2, noopLog)
s.Require().NoError(err)
pm = pruning.NewManager(sc, ss, nil, nil)
s.newStoreWithBackendMount(ss, sc, pm)
err = s.rootStore.LoadLatestVersion()
s.Require().Nil(err)
latestVer, ro, err := s.rootStore.StateLatest()
s.Require().Nil(err)
s.Require().Equal(uint64(4), latestVer)
reader, err := ro.GetReader(testStoreKeyBytes)
s.Require().NoError(err)
result, err := reader.Get([]byte(fmt.Sprintf("key%03d", 3)))
s.Require().NoError(err)
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 3, 1)), result, "value should be equal")
reader, err = ro.GetReader(testStoreKey2Bytes)
s.Require().NoError(err)
result, err = reader.Get([]byte(fmt.Sprintf("key%03d", 2)))
s.Require().NoError(err)
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 2, 2)), result, "value should be equal")
reader, err = ro.GetReader(testStoreKey3Bytes)
s.Require().NoError(err)
result, err = reader.Get([]byte(fmt.Sprintf("key%03d", 4)))
s.Require().NoError(err)
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 4, 3)), result, "value should be equal")
}
func (s *RootStoreTestSuite) TestHashStableWithEmptyCommit() {
err := s.rootStore.LoadLatestVersion()
s.Require().Nil(err)
commitID := proof.CommitID{}
lastCommitID, err := s.rootStore.LastCommitID()
s.Require().Nil(err)
s.Require().Equal(commitID, lastCommitID)
cs := corestore.NewChangeset()
cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false)
cHash, err := s.rootStore.Commit(cs)
s.Require().Nil(err)
s.Require().NotNil(cHash)
latestVersion, err := s.rootStore.GetLatestVersion()
hash := cHash
s.Require().Nil(err)
s.Require().Equal(uint64(1), latestVersion)
// make an empty commit, it should update version, but not affect hash
cHash, err = s.rootStore.Commit(corestore.NewChangeset())
s.Require().Nil(err)
s.Require().NotNil(cHash)
latestVersion, err = s.rootStore.GetLatestVersion()
s.Require().Nil(err)
s.Require().Equal(uint64(2), latestVersion)
s.Require().Equal(hash, cHash)
}