feat(store/v2): Fallback to SC on queries (#19090)
This commit is contained in:
parent
2587e756c1
commit
8d0696326b
@ -67,12 +67,21 @@ func (t *IavlTree) Commit() ([]byte, uint64, error) {
|
||||
|
||||
// GetProof returns a proof for the given key and version.
|
||||
func (t *IavlTree) GetProof(version uint64, key []byte) (*ics23.CommitmentProof, error) {
|
||||
imutableTree, err := t.tree.GetImmutable(int64(version))
|
||||
immutableTree, err := t.tree.GetImmutable(int64(version))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to get immutable tree at version %d: %w", version, err)
|
||||
}
|
||||
|
||||
return imutableTree.GetProof(key)
|
||||
return immutableTree.GetProof(key)
|
||||
}
|
||||
|
||||
func (t *IavlTree) Get(version uint64, key []byte) ([]byte, error) {
|
||||
immutableTree, err := t.tree.GetImmutable(int64(version))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get immutable tree at version %d: %w", version, err)
|
||||
}
|
||||
|
||||
return immutableTree.Get(key)
|
||||
}
|
||||
|
||||
// GetLatestVersion returns the latest version of the tree.
|
||||
|
||||
@ -57,6 +57,15 @@ func TestIavlTree(t *testing.T) {
|
||||
require.Equal(t, workingHash, commitHash)
|
||||
require.Equal(t, uint64(1), tree.GetLatestVersion())
|
||||
|
||||
// ensure we can get expected values
|
||||
bz, err := tree.Get(1, []byte("key1"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []byte("value1"), bz)
|
||||
|
||||
bz, err = tree.Get(2, []byte("key1"))
|
||||
require.Error(t, err)
|
||||
require.Nil(t, bz)
|
||||
|
||||
// write a batch of version 2
|
||||
require.NoError(t, tree.Set([]byte("key4"), []byte("value4")))
|
||||
require.NoError(t, tree.Set([]byte("key5"), []byte("value5")))
|
||||
|
||||
@ -255,6 +255,20 @@ func (c *CommitStore) GetProof(storeKey string, version uint64, key []byte) ([]s
|
||||
return []store.CommitmentOp{commitOp, *storeCommitmentOp}, nil
|
||||
}
|
||||
|
||||
func (c *CommitStore) Get(storeKey string, version uint64, key []byte) ([]byte, error) {
|
||||
tree, ok := c.multiTrees[storeKey]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("store %s not found", storeKey)
|
||||
}
|
||||
|
||||
bz, err := tree.Get(version, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get key %s from store %s: %w", key, storeKey, err)
|
||||
}
|
||||
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func (c *CommitStore) Prune(version uint64) (ferr error) {
|
||||
// prune the metadata
|
||||
batch := c.db.NewBatch()
|
||||
|
||||
@ -17,14 +17,24 @@ type Tree interface {
|
||||
Set(key, value []byte) error
|
||||
Remove(key []byte) error
|
||||
GetLatestVersion() uint64
|
||||
|
||||
// Hash returns the hash of the latest saved version of the tree.
|
||||
Hash() []byte
|
||||
|
||||
// WorkingHash returns the working hash of the tree.
|
||||
WorkingHash() []byte
|
||||
|
||||
LoadVersion(version uint64) error
|
||||
Commit() ([]byte, uint64, error)
|
||||
SetInitialVersion(version uint64) error
|
||||
GetProof(version uint64, key []byte) (*ics23.CommitmentProof, error)
|
||||
|
||||
// Get attempts to retrieve a value from the tree for a given version.
|
||||
//
|
||||
// NOTE: This method only exists to support migration from IAVL v0/v1 to v2.
|
||||
// Once migration is complete, this method should be removed and/or not used.
|
||||
Get(version uint64, key []byte) ([]byte, error)
|
||||
|
||||
Prune(version uint64) error
|
||||
Export(version uint64) (Exporter, error)
|
||||
Import(version uint64) (Importer, error)
|
||||
|
||||
@ -69,18 +69,31 @@ type VersionedDatabase interface {
|
||||
type Committer interface {
|
||||
// WriteBatch writes a batch of key-value pairs to the tree.
|
||||
WriteBatch(cs *Changeset) error
|
||||
|
||||
// WorkingCommitInfo returns the CommitInfo for the working tree.
|
||||
WorkingCommitInfo(version uint64) *CommitInfo
|
||||
|
||||
// GetLatestVersion returns the latest version.
|
||||
GetLatestVersion() (uint64, error)
|
||||
|
||||
// LoadVersion loads the tree at the given version.
|
||||
LoadVersion(targetVersion uint64) error
|
||||
|
||||
// Commit commits the working tree to the database.
|
||||
Commit(version uint64) (*CommitInfo, error)
|
||||
|
||||
// GetProof returns the proof of existence or non-existence for the given key.
|
||||
GetProof(storeKey string, version uint64, key []byte) ([]CommitmentOp, error)
|
||||
|
||||
// Get returns the value for the given key at the given version.
|
||||
//
|
||||
// NOTE: This method only exists to support migration from IAVL v0/v1 to v2.
|
||||
// Once migration is complete, this method should be removed and/or not used.
|
||||
Get(storeKey string, version uint64, key []byte) ([]byte, error)
|
||||
|
||||
// SetInitialVersion sets the initial version of the tree.
|
||||
SetInitialVersion(version uint64) error
|
||||
|
||||
// GetCommitInfo returns the CommitInfo for the given version.
|
||||
GetCommitInfo(version uint64) (*CommitInfo, error)
|
||||
|
||||
|
||||
@ -105,13 +105,13 @@ func (s *Store) StateLatest() (uint64, store.ReadOnlyRootStore, error) {
|
||||
}
|
||||
|
||||
func (s *Store) StateAt(v uint64) (store.ReadOnlyRootStore, error) {
|
||||
// TODO(bez): Ensure the version <v> exists. We can utilize the GetCommitInfo()
|
||||
// SC method once available.
|
||||
// TODO(bez): We may want to avoid relying on the SC metadata here. Instead,
|
||||
// we should add a VersionExists() method to the VersionedDatabase interface.
|
||||
//
|
||||
// Ref: https://github.com/cosmos/cosmos-sdk/pull/18736
|
||||
// if err := s.stateCommitment.GetCommitInfo(v); err != nil {
|
||||
// return nil, fmt.Errorf("failed to get commit info for version %d: %w", v, err)
|
||||
// }
|
||||
// Ref: https://github.com/cosmos/cosmos-sdk/issues/19091
|
||||
if cInfo, err := s.stateCommitment.GetCommitInfo(v); err != nil || cInfo == nil {
|
||||
return nil, fmt.Errorf("failed to get commit info for version %d: %w", v, err)
|
||||
}
|
||||
|
||||
return NewReadOnlyAdapter(v, s), nil
|
||||
}
|
||||
@ -174,8 +174,23 @@ func (s *Store) Query(storeKey string, version uint64, key []byte, prove bool) (
|
||||
}
|
||||
|
||||
val, err := s.stateStore.Get(storeKey, version, key)
|
||||
if err != nil {
|
||||
return store.QueryResult{}, err
|
||||
if err != nil || val == nil {
|
||||
// fallback to querying SC backend if not found in SS backend
|
||||
//
|
||||
// Note, this should only used during migration, i.e. while SS and IAVL v2
|
||||
// are being asynchronously synced.
|
||||
if val == nil {
|
||||
bz, scErr := s.stateCommitment.Get(storeKey, version, key)
|
||||
if scErr != nil {
|
||||
return store.QueryResult{}, fmt.Errorf("failed to query SC store: %w", scErr)
|
||||
}
|
||||
|
||||
val = bz
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return store.QueryResult{}, fmt.Errorf("failed to query SS store: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
result := store.QueryResult{
|
||||
@ -187,7 +202,7 @@ func (s *Store) Query(storeKey string, version uint64, key []byte, prove bool) (
|
||||
if prove {
|
||||
result.ProofOps, err = s.stateCommitment.GetProof(storeKey, version, key)
|
||||
if err != nil {
|
||||
return store.QueryResult{}, err
|
||||
return store.QueryResult{}, fmt.Errorf("failed to get SC store proof: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -104,6 +104,31 @@ func (s *RootStoreTestSuite) TestQuery() {
|
||||
s.Require().Equal([]byte("foo"), result.ProofOps[0].Key)
|
||||
}
|
||||
|
||||
func (s *RootStoreTestSuite) TestGetFallback() {
|
||||
sc := s.rootStore.GetStateCommitment()
|
||||
|
||||
// create a changeset and commit it to SC ONLY
|
||||
cs := store.NewChangeset()
|
||||
cs.Add(testStoreKey, []byte("foo"), []byte("bar"))
|
||||
|
||||
err := sc.WriteBatch(cs)
|
||||
s.Require().NoError(err)
|
||||
|
||||
ci := sc.WorkingCommitInfo(1)
|
||||
_, err = sc.Commit(ci.Version)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// ensure we can query for the key, which should fallback to SC
|
||||
qResult, err := s.rootStore.Query(testStoreKey, 1, []byte("foo"), false)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal([]byte("bar"), qResult.Value)
|
||||
|
||||
// non-existent key
|
||||
qResult, err = s.rootStore.Query(testStoreKey, 1, []byte("non_existent_key"), false)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Nil(qResult.Value)
|
||||
}
|
||||
|
||||
func (s *RootStoreTestSuite) TestQueryProof() {
|
||||
cs := store.NewChangeset()
|
||||
// testStoreKey
|
||||
|
||||
Loading…
Reference in New Issue
Block a user