feat(store/v2): Modify RootStore to Support Multi StoreKeys (#18968)
This commit is contained in:
+55
-48
@@ -18,11 +18,6 @@ import (
|
||||
"cosmossdk.io/store/v2/pruning"
|
||||
)
|
||||
|
||||
// defaultStoreKey defines the default store key used for the single SC backend.
|
||||
// Note, however, this store key is essentially irrelevant as it's not exposed
|
||||
// to the user and it only needed to fulfill usage of StoreInfo during Commit.
|
||||
const defaultStoreKey = "default"
|
||||
|
||||
var _ store.RootStore = (*Store)(nil)
|
||||
|
||||
// Store defines the SDK's default RootStore implementation. It contains a single
|
||||
@@ -39,9 +34,10 @@ type Store struct {
|
||||
// stateCommitment reflects the state commitment (SC) backend
|
||||
stateCommitment store.Committer
|
||||
|
||||
// rootKVStore reflects the root BranchedKVStore that is used to accumulate writes
|
||||
// kvStores reflects a mapping of store keys, typically dedicated to modules,
|
||||
// to a dedicated BranchedKVStore. Each store is used to accumulate writes
|
||||
// and branch off of.
|
||||
rootKVStore store.BranchedKVStore
|
||||
kvStores map[string]store.BranchedKVStore
|
||||
|
||||
// commitHeader reflects the header used when committing state (note, this isn't required and only used for query purposes)
|
||||
commitHeader *coreheader.Info
|
||||
@@ -69,12 +65,18 @@ func New(
|
||||
logger log.Logger,
|
||||
ss store.VersionedDatabase,
|
||||
sc store.Committer,
|
||||
storeKeys []string,
|
||||
ssOpts, scOpts pruning.Options,
|
||||
m metrics.StoreMetrics,
|
||||
) (store.RootStore, error) {
|
||||
rootKVStore, err := branch.New(defaultStoreKey, ss)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
kvStores := make(map[string]store.BranchedKVStore, len(storeKeys))
|
||||
for _, storeKey := range storeKeys {
|
||||
bkv, err := branch.New(storeKey, ss)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kvStores[storeKey] = bkv
|
||||
}
|
||||
|
||||
pruningManager := pruning.NewManager(logger, ss, sc)
|
||||
@@ -87,7 +89,7 @@ func New(
|
||||
initialVersion: 1,
|
||||
stateStore: ss,
|
||||
stateCommitment: sc,
|
||||
rootKVStore: rootKVStore,
|
||||
kvStores: kvStores,
|
||||
pruningManager: pruningManager,
|
||||
telemetry: m,
|
||||
}, nil
|
||||
@@ -196,24 +198,28 @@ func (s *Store) Query(storeKey string, version uint64, key []byte, prove bool) (
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetKVStore returns the store's root KVStore. Any writes to this store without
|
||||
// branching will be committed to SC and SS upon Commit(). Branching will create
|
||||
// GetKVStore returns a KVStore for the given store key. Any writes to this store
|
||||
// without branching will be committed to SC and SS upon Commit(). Branching will create
|
||||
// a branched KVStore that allow writes to be discarded and propagated to the
|
||||
// root KVStore using Write().
|
||||
func (s *Store) GetKVStore(_ string) store.KVStore {
|
||||
if s.TracingEnabled() {
|
||||
return trace.New(s.rootKVStore, s.traceWriter, s.traceContext)
|
||||
func (s *Store) GetKVStore(storeKey string) store.KVStore {
|
||||
bkv, ok := s.kvStores[storeKey]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown store key: %s", storeKey))
|
||||
}
|
||||
|
||||
return s.rootKVStore
|
||||
if s.TracingEnabled() {
|
||||
return trace.New(bkv, s.traceWriter, s.traceContext)
|
||||
}
|
||||
|
||||
return bkv
|
||||
}
|
||||
|
||||
func (s *Store) GetBranchedKVStore(_ string) store.BranchedKVStore {
|
||||
if s.TracingEnabled() {
|
||||
return trace.New(s.rootKVStore, s.traceWriter, s.traceContext)
|
||||
}
|
||||
|
||||
return s.rootKVStore
|
||||
func (s *Store) GetBranchedKVStore(storeKey string) store.BranchedKVStore {
|
||||
// Branching will soon be removed.
|
||||
//
|
||||
// Ref: https://github.com/cosmos/cosmos-sdk/issues/18981
|
||||
panic("TODO: WILL BE REMOVED!")
|
||||
}
|
||||
|
||||
func (s *Store) LoadLatestVersion() error {
|
||||
@@ -242,10 +248,12 @@ func (s *Store) LoadVersion(version uint64) error {
|
||||
func (s *Store) loadVersion(v uint64) error {
|
||||
s.logger.Debug("loading version", "version", v)
|
||||
|
||||
// Reset the root KVStore s.t. the latest version is v. Any writes will
|
||||
// overwrite existing versions.
|
||||
if err := s.rootKVStore.Reset(v); err != nil {
|
||||
return err
|
||||
// Reset each KVStore s.t. the latest version is v. Any writes will overwrite
|
||||
// existing versions.
|
||||
for storeKey, kvStore := range s.kvStores {
|
||||
if err := kvStore.Reset(v); err != nil {
|
||||
return fmt.Errorf("failed to reset %s KVStore: %w", storeKey, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.stateCommitment.LoadVersion(v); err != nil {
|
||||
@@ -277,22 +285,11 @@ func (s *Store) SetCommitHeader(h *coreheader.Info) {
|
||||
s.commitHeader = h
|
||||
}
|
||||
|
||||
// Branch a copy of the Store with a branched underlying root KVStore. Any call
|
||||
// to GetKVStore and GetBranchedKVStore returns the branched KVStore.
|
||||
func (s *Store) Branch() store.BranchedRootStore {
|
||||
branch := s.rootKVStore.Branch()
|
||||
|
||||
return &Store{
|
||||
logger: s.logger,
|
||||
initialVersion: s.initialVersion,
|
||||
stateStore: s.stateStore,
|
||||
stateCommitment: s.stateCommitment,
|
||||
rootKVStore: branch,
|
||||
commitHeader: s.commitHeader,
|
||||
lastCommitInfo: s.lastCommitInfo,
|
||||
traceWriter: s.traceWriter,
|
||||
traceContext: s.traceContext,
|
||||
}
|
||||
// Branching will soon be removed.
|
||||
//
|
||||
// Ref: https://github.com/cosmos/cosmos-sdk/issues/18981
|
||||
panic("TODO: WILL BE REMOVED!")
|
||||
}
|
||||
|
||||
// WorkingHash returns the working hash of the root store. Note, WorkingHash()
|
||||
@@ -320,7 +317,9 @@ func (s *Store) WorkingHash() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (s *Store) Write() {
|
||||
s.rootKVStore.Write()
|
||||
for _, kvStore := range s.kvStores {
|
||||
kvStore.Write()
|
||||
}
|
||||
}
|
||||
|
||||
// Commit commits all state changes to the underlying SS and SC backends. Note,
|
||||
@@ -347,7 +346,10 @@ func (s *Store) Commit() ([]byte, error) {
|
||||
s.logger.Debug("commit header and version mismatch", "header_height", s.commitHeader.Height, "version", version)
|
||||
}
|
||||
|
||||
changeset := s.rootKVStore.GetChangeset()
|
||||
changeset := store.NewChangeset()
|
||||
for _, kvStore := range s.kvStores {
|
||||
changeset.Merge(kvStore.GetChangeset())
|
||||
}
|
||||
|
||||
// commit SS
|
||||
if err := s.stateStore.ApplyChangeset(version, changeset); err != nil {
|
||||
@@ -363,8 +365,10 @@ func (s *Store) Commit() ([]byte, error) {
|
||||
s.lastCommitInfo.Timestamp = s.commitHeader.Time
|
||||
}
|
||||
|
||||
if err := s.rootKVStore.Reset(version); err != nil {
|
||||
return nil, fmt.Errorf("failed to reset root KVStore: %w", err)
|
||||
for storeKey, kvStore := range s.kvStores {
|
||||
if err := kvStore.Reset(version); err != nil {
|
||||
return nil, fmt.Errorf("failed to reset %s KVStore: %w", storeKey, err)
|
||||
}
|
||||
}
|
||||
|
||||
s.workingHash = nil
|
||||
@@ -380,9 +384,12 @@ func (s *Store) Commit() ([]byte, error) {
|
||||
// of the SC tree. Finally, we construct a *CommitInfo and return the hash.
|
||||
// Note, this should only be called once per block!
|
||||
func (s *Store) writeSC() error {
|
||||
changeSet := s.rootKVStore.GetChangeset()
|
||||
changeset := store.NewChangeset()
|
||||
for _, kvStore := range s.kvStores {
|
||||
changeset.Merge(kvStore.GetChangeset())
|
||||
}
|
||||
|
||||
if err := s.stateCommitment.WriteBatch(changeSet); err != nil {
|
||||
if err := s.stateCommitment.WriteBatch(changeset); err != nil {
|
||||
return fmt.Errorf("failed to write batch to SC store: %w", err)
|
||||
}
|
||||
|
||||
|
||||
+16
-98
@@ -17,6 +17,10 @@ import (
|
||||
"cosmossdk.io/store/v2/storage/sqlite"
|
||||
)
|
||||
|
||||
const (
|
||||
testStoreKey = "test"
|
||||
)
|
||||
|
||||
type RootStoreTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
@@ -35,10 +39,10 @@ func (s *RootStoreTestSuite) SetupTest() {
|
||||
ss := storage.NewStorageStore(sqliteDB)
|
||||
|
||||
tree := iavl.NewIavlTree(dbm.NewMemDB(), noopLog, iavl.DefaultConfig())
|
||||
sc, err := commitment.NewCommitStore(map[string]commitment.Tree{defaultStoreKey: tree}, noopLog)
|
||||
sc, err := commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree}, noopLog)
|
||||
s.Require().NoError(err)
|
||||
|
||||
rs, err := New(noopLog, ss, sc, pruning.DefaultOptions(), pruning.DefaultOptions(), nil)
|
||||
rs, err := New(noopLog, ss, sc, []string{testStoreKey}, pruning.DefaultOptions(), pruning.DefaultOptions(), nil)
|
||||
s.Require().NoError(err)
|
||||
|
||||
rs.SetTracer(io.Discard)
|
||||
@@ -59,22 +63,16 @@ func (s *RootStoreTestSuite) TestGetSCStore() {
|
||||
}
|
||||
|
||||
func (s *RootStoreTestSuite) TestGetKVStore() {
|
||||
kvs := s.rootStore.GetKVStore("")
|
||||
kvs := s.rootStore.GetKVStore(testStoreKey)
|
||||
s.Require().NotNil(kvs)
|
||||
}
|
||||
|
||||
func (s *RootStoreTestSuite) TestGetBranchedKVStore() {
|
||||
bs := s.rootStore.GetBranchedKVStore("")
|
||||
s.Require().NotNil(bs)
|
||||
s.Require().Empty(bs.GetChangeset().Size())
|
||||
}
|
||||
|
||||
func (s *RootStoreTestSuite) TestQuery() {
|
||||
_, err := s.rootStore.Query("", 1, []byte("foo"), true)
|
||||
s.Require().Error(err)
|
||||
|
||||
// write and commit a changeset
|
||||
bs := s.rootStore.GetBranchedKVStore("")
|
||||
bs := s.rootStore.GetKVStore(testStoreKey)
|
||||
bs.Set([]byte("foo"), []byte("bar"))
|
||||
|
||||
workingHash, err := s.rootStore.WorkingHash()
|
||||
@@ -87,51 +85,17 @@ func (s *RootStoreTestSuite) TestQuery() {
|
||||
s.Require().Equal(workingHash, commitHash)
|
||||
|
||||
// ensure the proof is non-nil for the corresponding version
|
||||
result, err := s.rootStore.Query(defaultStoreKey, 1, []byte("foo"), true)
|
||||
result, err := s.rootStore.Query(testStoreKey, 1, []byte("foo"), true)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(result.Proof.Proof)
|
||||
s.Require().Equal([]byte("foo"), result.Proof.Proof.GetExist().Key)
|
||||
s.Require().Equal([]byte("bar"), result.Proof.Proof.GetExist().Value)
|
||||
}
|
||||
|
||||
func (s *RootStoreTestSuite) TestBranch() {
|
||||
// write and commit a changeset
|
||||
bs := s.rootStore.GetKVStore("")
|
||||
bs.Set([]byte("foo"), []byte("bar"))
|
||||
|
||||
workingHash, err := s.rootStore.WorkingHash()
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(workingHash)
|
||||
|
||||
commitHash, err := s.rootStore.Commit()
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(commitHash)
|
||||
s.Require().Equal(workingHash, commitHash)
|
||||
|
||||
// branch the root store
|
||||
rs2 := s.rootStore.Branch()
|
||||
|
||||
// ensure we can perform reads which pass through to the original root store
|
||||
bs2 := rs2.GetKVStore("")
|
||||
s.Require().Equal([]byte("bar"), bs2.Get([]byte("foo")))
|
||||
|
||||
// make a change to the branched root store
|
||||
bs2.Set([]byte("foo"), []byte("updated_bar"))
|
||||
|
||||
// ensure the original root store is not modified
|
||||
s.Require().Equal([]byte("bar"), bs.Get([]byte("foo")))
|
||||
|
||||
// write changes
|
||||
rs2.Write()
|
||||
|
||||
// ensure changes are reflected in the original root store
|
||||
s.Require().Equal([]byte("updated_bar"), bs.Get([]byte("foo")))
|
||||
}
|
||||
|
||||
func (s *RootStoreTestSuite) TestLoadVersion() {
|
||||
// write and commit a few changesets
|
||||
for v := 1; v <= 5; v++ {
|
||||
bs := s.rootStore.GetBranchedKVStore("")
|
||||
bs := s.rootStore.GetKVStore(testStoreKey)
|
||||
val := fmt.Sprintf("val%03d", v) // val001, val002, ..., val005
|
||||
bs.Set([]byte("key"), []byte(val))
|
||||
|
||||
@@ -164,13 +128,13 @@ func (s *RootStoreTestSuite) TestLoadVersion() {
|
||||
s.Require().Equal(uint64(3), latest)
|
||||
|
||||
// query state and ensure values returned are based on the loaded version
|
||||
kvStore := s.rootStore.GetKVStore("")
|
||||
kvStore := s.rootStore.GetKVStore(testStoreKey)
|
||||
val := kvStore.Get([]byte("key"))
|
||||
s.Require().Equal([]byte("val003"), val)
|
||||
|
||||
// attempt to write and commit a few changesets
|
||||
for v := 4; v <= 5; v++ {
|
||||
bs := s.rootStore.GetBranchedKVStore("")
|
||||
bs := s.rootStore.GetKVStore(testStoreKey)
|
||||
val := fmt.Sprintf("overwritten_val%03d", v) // overwritten_val004, overwritten_val005
|
||||
bs.Set([]byte("key"), []byte(val))
|
||||
|
||||
@@ -190,61 +154,18 @@ func (s *RootStoreTestSuite) TestLoadVersion() {
|
||||
s.Require().Equal(uint64(5), latest)
|
||||
|
||||
// query state and ensure values returned are based on the loaded version
|
||||
kvStore = s.rootStore.GetKVStore("")
|
||||
kvStore = s.rootStore.GetKVStore(testStoreKey)
|
||||
val = kvStore.Get([]byte("key"))
|
||||
s.Require().Equal([]byte("overwritten_val005"), val)
|
||||
}
|
||||
|
||||
func (s *RootStoreTestSuite) TestMultiBranch() {
|
||||
// write and commit a changeset
|
||||
bs := s.rootStore.GetKVStore("")
|
||||
bs.Set([]byte("foo"), []byte("bar"))
|
||||
|
||||
workingHash, err := s.rootStore.WorkingHash()
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(workingHash)
|
||||
|
||||
commitHash, err := s.rootStore.Commit()
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(commitHash)
|
||||
s.Require().Equal(workingHash, commitHash)
|
||||
|
||||
// create multiple branches of the root store
|
||||
var branchedRootStores []store.BranchedRootStore
|
||||
for i := 0; i < 5; i++ {
|
||||
branchedRootStores = append(branchedRootStores, s.rootStore.Branch())
|
||||
}
|
||||
|
||||
// get the last branched root store
|
||||
rs2 := branchedRootStores[4]
|
||||
|
||||
// ensure we can perform reads which pass through to the original root store
|
||||
bs2 := rs2.GetKVStore("")
|
||||
s.Require().Equal([]byte("bar"), bs2.Get([]byte("foo")))
|
||||
|
||||
// make a change to the branched root store
|
||||
bs2.Set([]byte("foo"), []byte("updated_bar"))
|
||||
|
||||
// ensure the original root store is not modified
|
||||
s.Require().Equal([]byte("bar"), bs.Get([]byte("foo")))
|
||||
|
||||
// write changes
|
||||
rs2.Write()
|
||||
|
||||
// ensure changes are reflected in the original root store
|
||||
s.Require().Equal([]byte("updated_bar"), bs.Get([]byte("foo")))
|
||||
}
|
||||
|
||||
func (s *RootStoreTestSuite) TestCommit() {
|
||||
lv, err := s.rootStore.GetLatestVersion()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Zero(lv)
|
||||
|
||||
// branch the root store
|
||||
rs2 := s.rootStore.Branch()
|
||||
|
||||
// perform changes
|
||||
bs2 := rs2.GetKVStore("")
|
||||
bs2 := s.rootStore.GetKVStore(testStoreKey)
|
||||
for i := 0; i < 100; i++ {
|
||||
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
|
||||
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099
|
||||
@@ -252,9 +173,6 @@ func (s *RootStoreTestSuite) TestCommit() {
|
||||
bs2.Set([]byte(key), []byte(val))
|
||||
}
|
||||
|
||||
// write to the branched root store, which will flush to the parent root store
|
||||
rs2.Write()
|
||||
|
||||
// committing w/o calling WorkingHash should error
|
||||
_, err = s.rootStore.Commit()
|
||||
s.Require().Error(err)
|
||||
@@ -273,10 +191,10 @@ func (s *RootStoreTestSuite) TestCommit() {
|
||||
s.Require().Equal(uint64(1), lv)
|
||||
|
||||
// ensure the root KVStore is cleared
|
||||
s.Require().Empty(s.rootStore.(*Store).rootKVStore.GetChangeset().Size())
|
||||
s.Require().Empty(s.rootStore.(*Store).kvStores[testStoreKey].GetChangeset().Size())
|
||||
|
||||
// perform reads on the updated root store
|
||||
bs := s.rootStore.GetKVStore("")
|
||||
bs := s.rootStore.GetKVStore(testStoreKey)
|
||||
for i := 0; i < 100; i++ {
|
||||
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
|
||||
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099
|
||||
|
||||
Reference in New Issue
Block a user