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

feat(store/v2): implement the feature to upgrade the store keys #20453

Merged
merged 46 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
34f301a
init
cool-develope May 25, 2024
e22fa63
Merge branch 'main' into store/upgrade
cool-develope May 29, 2024
deb1cef
wip
cool-develope May 31, 2024
422c2f1
Merge branch 'main' into store/upgrade
cool-develope May 31, 2024
de5188c
add tests
cool-develope Jun 3, 2024
787c2f1
Merge branch 'main' into store/upgrade
cool-develope Jun 3, 2024
cc5803d
lint
cool-develope Jun 3, 2024
982234c
feat(store/v2): Implement the feature to upgrade `storeKeys` on the s…
cool-develope Jun 11, 2024
52a0148
Merge branch 'main' into store/upgrade
cool-develope Jun 11, 2024
c434513
address comments
cool-develope Jun 12, 2024
01f58fe
Merge branch 'main' into store/upgrade
cool-develope Jun 12, 2024
0a395e0
error handle
cool-develope Jun 12, 2024
9b91446
Merge branch 'main' into store/upgrade
cool-develope Jun 19, 2024
fee3fd7
comments
cool-develope Jun 19, 2024
9ddef3c
Merge branch 'main' into store/upgrade
cool-develope Jun 24, 2024
cc04a80
Merge branch 'main' into store/upgrade
cool-develope Jun 27, 2024
5231c52
comments
cool-develope Jun 27, 2024
37079b9
Merge branch 'main' into store/upgrade
cool-develope Jun 27, 2024
e23e66a
comments
cool-develope Jun 28, 2024
5249bcd
Merge branch 'main' into store/upgrade
cool-develope Jul 1, 2024
b74f833
Merge branch 'main' into store/upgrade
cool-develope Jul 3, 2024
dd22733
wrap up
cool-develope Jul 3, 2024
6f23f8e
Merge branch 'main' into store/upgrade
cool-develope Jul 8, 2024
0b11f85
feat(store/v2): Removing old store keys by pruning (#20927)
cool-develope Jul 24, 2024
ad0310d
remove rename feature
cool-develope Jul 24, 2024
5e5b340
Merge branch 'main' into store/upgrade
cool-develope Jul 24, 2024
2af4c06
wip
cool-develope Jul 24, 2024
7240ced
fixing
cool-develope Jul 24, 2024
4309308
minor fix
cool-develope Jul 24, 2024
6d81e39
minor cleanup
cool-develope Jul 24, 2024
e99168d
linting
cool-develope Jul 24, 2024
77d0533
Merge branch 'main' into store/upgrade
cool-develope Jul 25, 2024
f70c4fa
Merge branch 'main' into store/upgrade
cool-develope Jul 29, 2024
2d341ae
go mod tidy
cool-develope Jul 29, 2024
6ae8def
docs
cool-develope Jul 30, 2024
805f697
Merge branch 'main' into store/upgrade
cool-develope Jul 30, 2024
d3c6e87
revert go.mod update
cool-develope Jul 30, 2024
2e1d53b
go mod update
cool-develope Jul 31, 2024
be3e5af
Merge branch 'main' into store/upgrade
cool-develope Jul 31, 2024
0948f11
iavl version match
cool-develope Aug 1, 2024
74b31e0
Merge branch 'main' into store/upgrade
cool-develope Aug 1, 2024
66d86ec
Update store/v2/README.md
cool-develope Aug 1, 2024
f50b207
go mod update
cool-develope Aug 1, 2024
bca11e4
remove mountTreeFn
cool-develope Aug 5, 2024
32fbcfe
Merge branch 'main' into store/upgrade
cool-develope Aug 5, 2024
65bce60
linting
cool-develope Aug 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions core/store/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ func (s *StoreUpgrades) IsDeleted(key string) bool {
return false
}

// IsRenamed returns true if the given key should be renamed
func (s *StoreUpgrades) IsRenamed(key string) bool {
if s == nil {
alpe marked this conversation as resolved.
Show resolved Hide resolved
return false
}
for _, re := range s.Renamed {
if re.OldKey == key {
return true
}
}
return false
}

alpe marked this conversation as resolved.
Show resolved Hide resolved
// RenamedFrom returns the oldKey if it was renamed
// Returns "" if it was not renamed
func (s *StoreUpgrades) RenamedFrom(key string) string {
Expand Down
12 changes: 10 additions & 2 deletions store/v2/commitment/iavl/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,31 @@ import (
)

var (
_ commitment.Tree = (*IavlTree)(nil)
_ store.PausablePruner = (*IavlTree)(nil)
_ commitment.Tree = (*IavlTree)(nil)
_ commitment.KVStoreGetter = (*IavlTree)(nil)
_ store.PausablePruner = (*IavlTree)(nil)
)

// IavlTree is a wrapper around iavl.MutableTree.
type IavlTree struct {
tree *iavl.MutableTree
db corestore.KVStoreWithBatch
}

// NewIavlTree creates a new IavlTree instance.
func NewIavlTree(db corestore.KVStoreWithBatch, logger log.Logger, cfg *Config) *IavlTree {
tree := iavl.NewMutableTree(dbm.NewWrapper(db), cfg.CacheSize, cfg.SkipFastStorageUpgrade, logger, iavl.AsyncPruningOption(true))
return &IavlTree{
tree: tree,
db: db,
}
}

// GetKVStoreWithBatch implements the KVStoreGetter interface.
func (t *IavlTree) GetKVStoreWithBatch() corestore.KVStoreWithBatch {
return t.db
}

// Remove removes the given key from the tree.
func (t *IavlTree) Remove(key []byte) error {
_, _, err := t.tree.Remove(key)
Expand Down
165 changes: 155 additions & 10 deletions store/v2/commitment/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"fmt"
"io"
"math"
"sort"

protoio "github.com/cosmos/gogoproto/io"

Expand All @@ -23,12 +24,15 @@
const (
commitInfoKeyFmt = "c/%d" // c/<version>
latestVersionKey = "c/latest"

batchFlushThreshold = 1 << 16 // 64KB
)

var (
_ store.Committer = (*CommitStore)(nil)
_ snapshots.CommitSnapshotter = (*CommitStore)(nil)
_ store.PausablePruner = (*CommitStore)(nil)
_ store.KVStoreGetter = (*CommitStore)(nil)
)

// CommitStore is a wrapper around multiple Tree objects mapped by a unique store
Expand Down Expand Up @@ -126,7 +130,7 @@
}
}

func (c *CommitStore) LoadVersion(targetVersion uint64) error {
func (c *CommitStore) LoadVersion(targetVersion uint64, upgrades *corestore.StoreUpgrades) error {
// Rollback the metadata to the target version.
latestVersion, err := c.GetLatestVersion()
if err != nil {
Expand All @@ -146,20 +150,150 @@
}
}

for _, tree := range c.multiTrees {
if err := tree.LoadVersion(targetVersion); err != nil {
// If the target version is greater than the latest version, it is the snapshot
// restore case, we should create a new commit info for the target version.
if targetVersion > latestVersion {
cInfo := c.WorkingCommitInfo(targetVersion)

for _, tree := range c.multiTrees {
if err := tree.LoadVersion(targetVersion); err != nil {
return err
}
}
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show resolved Hide resolved

return c.flushCommitInfo(targetVersion, cInfo)
}

storesKeys := make([]string, 0, len(c.multiTrees))
cool-develope marked this conversation as resolved.
Show resolved Hide resolved
for storeKey := range c.multiTrees {
alpe marked this conversation as resolved.
Show resolved Hide resolved
storesKeys = append(storesKeys, storeKey)
}

if upgrades != nil {
// deterministic iteration order for upgrades
// (as the underlying store may change and
// upgrades make store changes where the execution order may matter)
sort.Slice(storesKeys, func(i, j int) bool {
return storesKeys[i] < storesKeys[j]
})
}

commitInfo, err := c.GetCommitInfo(targetVersion)
if err != nil {
return err
}

for _, storeKey := range storesKeys {
// If it has been renamed, continue to the next store key.
if upgrades.IsRenamed(storeKey) {
continue
}

tree := c.multiTrees[storeKey]
alpe marked this conversation as resolved.
Show resolved Hide resolved

removeTree := func(storeKey string) error {
cool-develope marked this conversation as resolved.
Show resolved Hide resolved
if oldTree, ok := c.multiTrees[storeKey]; ok {
if err := oldTree.Close(); err != nil {
return err
}
delete(c.multiTrees, storeKey)
}
return nil
}

// If it has been deleted, remove the tree.
if upgrades.IsDeleted(storeKey) {
if err := removeTree(storeKey); err != nil {
return err
}
continue
}

// If it has been added, set the initial version.
if upgrades.IsAdded(storeKey) {
if err := tree.SetInitialVersion(targetVersion + 1); err != nil {
alpe marked this conversation as resolved.
Show resolved Hide resolved
return err
}
}

commitID := commitInfo.GetStoreCommitID([]byte(storeKey))
// If it has been renamed, migrate the data.
if oldKey := upgrades.RenamedFrom(storeKey); oldKey != "" {
alpe marked this conversation as resolved.
Show resolved Hide resolved
if err := c.migrateKVStore(oldKey, storeKey); err != nil {
return err
}
if err := removeTree(oldKey); err != nil {
return err
}
commitID = commitInfo.GetStoreCommitID([]byte(oldKey))
}

if err := tree.LoadVersion(commitID.Version); err != nil {
return err
}
}

alpe marked this conversation as resolved.
Show resolved Hide resolved
// If the target version is greater than the latest version, it is the snapshot
// restore case, we should create a new commit info for the target version.
var cInfo *proof.CommitInfo
if targetVersion > latestVersion {
cInfo = c.WorkingCommitInfo(targetVersion)
return nil
}

// migrateKVStore migrates the data from the old key to the new key.
// It should be synchronous to ensure that the data is migrated before the
// old store is pruned.
func (c *CommitStore) migrateKVStore(oldKey, newKey string) error {
var (
oldKVStore corestore.KVStoreWithBatch
newKVStore corestore.KVStoreWithBatch
)

if oldTree, ok := c.multiTrees[oldKey]; ok {
if getter, ok := oldTree.(KVStoreGetter); ok {
oldKVStore = getter.GetKVStoreWithBatch()
}
}

if newTree, ok := c.multiTrees[newKey]; ok {
if getter, ok := newTree.(KVStoreGetter); ok {
newKVStore = getter.GetKVStoreWithBatch()
}
}

return c.flushCommitInfo(targetVersion, cInfo)
if oldKVStore != nil && newKVStore != nil {
cool-develope marked this conversation as resolved.
Show resolved Hide resolved
iter, err := oldKVStore.Iterator(nil, nil)
if err != nil {
return err
}
defer func() {
_ = iter.Close()
}()

batch := newKVStore.NewBatch()
for ; iter.Valid(); iter.Next() {
if err := batch.Set(iter.Key(), iter.Value()); err != nil {
return err
}
bs, err := batch.GetByteSize()
if err != nil {
return err
}
if bs > batchFlushThreshold {
if err := batch.Write(); err != nil {
return err
}
if err := batch.Close(); err != nil {
return err
}
batch = newKVStore.NewBatch()
}
}
if err := batch.Write(); err != nil {
return err
}
if err := batch.Close(); err != nil {
return err
}
}

return nil
cool-develope marked this conversation as resolved.
Show resolved Hide resolved
}

func (c *CommitStore) GetCommitInfo(version uint64) (*proof.CommitInfo, error) {
Expand Down Expand Up @@ -490,7 +624,18 @@
}
}

return snapshotItem, c.LoadVersion(version)
return snapshotItem, c.LoadVersion(version, nil)
}

// GetKVStoreWithBatch implements store.KVStoreGetter.
func (c *CommitStore) GetKVStoreWithBatch(storeKey string) corestore.KVStoreWithBatch {
if tree, ok := c.multiTrees[storeKey]; ok {
if kvGetter, ok := tree.(KVStoreGetter); ok {
return kvGetter.GetKVStoreWithBatch()
}
}

return nil
}

func (c *CommitStore) Close() (ferr error) {
Expand Down
61 changes: 61 additions & 0 deletions store/v2/commitment/store_test_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
const (
storeKey1 = "store1"
storeKey2 = "store2"
storeKey3 = "store3"
)

// CommitStoreTestSuite is a test suite to be used for all tree backends.
Expand Down Expand Up @@ -166,3 +167,63 @@ func (s *CommitStoreTestSuite) TestStore_Pruning() {
}
}
}

func (s *CommitStoreTestSuite) TestStore_Upgrades() {
storeKeys := []string{storeKey1, storeKey2, storeKey3}
commitDB := dbm.NewMemDB()
commitStore, err := s.NewStore(commitDB, storeKeys, log.NewNopLogger())
s.Require().NoError(err)

latestVersion := uint64(10)
kvCount := 10
for i := uint64(1); i <= latestVersion; i++ {
kvPairs := make(map[string]corestore.KVPairs)
for _, storeKey := range storeKeys {
kvPairs[storeKey] = corestore.KVPairs{}
for j := 0; j < kvCount; j++ {
key := []byte(fmt.Sprintf("key-%d-%d", i, j))
value := []byte(fmt.Sprintf("value-%d-%d", i, j))
kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value})
}
}
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(kvPairs)))
_, err = commitStore.Commit(i)
s.Require().NoError(err)
}

// create a new commitment store with upgrades
upgrades := &corestore.StoreUpgrades{
Added: []string{"newStore1", "newStore2"},
Renamed: []corestore.StoreRename{
{OldKey: storeKey1, NewKey: "renamedStore1"},
},
Deleted: []string{storeKey3},
}
newStoreKeys := []string{"store1", "store2", "store3", "renamedStore1", "newStore1", "newStore2"}
cool-develope marked this conversation as resolved.
Show resolved Hide resolved
realStoreKeys := []string{"renamedStore1", "store2", "newStore1", "newStore2"}
cool-develope marked this conversation as resolved.
Show resolved Hide resolved
commitStore, err = s.NewStore(commitDB, newStoreKeys, log.NewNopLogger())
s.Require().NoError(err)
err = commitStore.LoadVersion(latestVersion, upgrades)
s.Require().NoError(err)

// apply the changeset again
cool-develope marked this conversation as resolved.
Show resolved Hide resolved
for i := latestVersion; i < latestVersion*2; i++ {
kvPairs := make(map[string]corestore.KVPairs)
for _, storeKey := range realStoreKeys {
kvPairs[storeKey] = corestore.KVPairs{}
for j := 0; j < kvCount; j++ {
key := []byte(fmt.Sprintf("key-%d-%d", i, j))
value := []byte(fmt.Sprintf("value-%d-%d", i, j))
kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value})
}
}
s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(kvPairs)))
commitInfo, err := commitStore.Commit(i)
s.Require().NoError(err)
s.Require().NotNil(commitInfo)
s.Require().Equal(len(realStoreKeys), len(commitInfo.StoreInfos))
for _, storeKey := range realStoreKeys {
s.Require().NotNil(commitInfo.GetStoreCommitID([]byte(storeKey)))
}
}
}
8 changes: 8 additions & 0 deletions store/v2/commitment/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

ics23 "github.com/cosmos/ics23/go"

corestore "cosmossdk.io/core/store"
snapshotstypes "cosmossdk.io/store/v2/snapshots/types"
)

Expand Down Expand Up @@ -56,3 +57,10 @@ type Importer interface {

io.Closer
}

// KVStoreGetter is an interface that allows getting the KVStoreWithBatch from a Tree.
// It is used to allow the commitment Tree to be used as a KVStore which can be used
// in the upgrade module.
type KVStoreGetter interface {
GetKVStoreWithBatch() corestore.KVStoreWithBatch
}
Loading
Loading