Skip to content

Commit

Permalink
feat(store/v2): add version exists (#22235)
Browse files Browse the repository at this point in the history
Co-authored-by: Cool Developer <cool199966@outlook.com>
  • Loading branch information
tac0turtle and cool-develope authored Oct 16, 2024
1 parent abaccb4 commit 76529d7
Show file tree
Hide file tree
Showing 17 changed files with 237 additions and 32 deletions.
2 changes: 1 addition & 1 deletion runtime/v2/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type Store interface {
Query(storeKey []byte, version uint64, key []byte, prove bool) (storev2.QueryResult, error)

// GetStateStorage returns the SS backend.
GetStateStorage() storev2.VersionedDatabase
GetStateStorage() storev2.VersionedWriter

// GetStateCommitment returns the SC backend.
GetStateCommitment() storev2.Committer
Expand Down
1 change: 1 addition & 0 deletions store/v2/commitment/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ func (c *CommitStore) GetProof(storeKey []byte, version uint64, key []byte) ([]p
return []proof.CommitmentOp{commitOp, *storeCommitmentOp}, nil
}

// Get implements store.VersionedReader.
func (c *CommitStore) Get(storeKey []byte, version uint64, key []byte) ([]byte, error) {
tree, ok := c.multiTrees[conv.UnsafeBytesToStr(storeKey)]
if !ok {
Expand Down
28 changes: 17 additions & 11 deletions store/v2/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,29 @@ import (
"cosmossdk.io/store/v2/proof"
)

// VersionedDatabase defines an API for a versioned database that allows reads,
// VersionedWriter defines an API for a versioned database that allows reads,
// writes, iteration and commitment over a series of versions.
type VersionedDatabase interface {
type VersionedWriter interface {
VersionedReader

SetLatestVersion(version uint64) error
ApplyChangeset(version uint64, cs *corestore.Changeset) error

// Close releases associated resources. It should NOT be idempotent. It must
// only be called once and any call after may panic.
io.Closer
}

type VersionedReader interface {
Has(storeKey []byte, version uint64, key []byte) (bool, error)
Get(storeKey []byte, version uint64, key []byte) ([]byte, error)

GetLatestVersion() (uint64, error)
SetLatestVersion(version uint64) error
VersionExists(v uint64) (bool, error)

Iterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error)
ReverseIterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error)

ApplyChangeset(version uint64, cs *corestore.Changeset) error

// Close releases associated resources. It should NOT be idempotent. It must
// only be called once and any call after may panic.
io.Closer
Expand Down Expand Up @@ -53,18 +63,14 @@ type Committer interface {
// GetProof returns the proof of existence or non-existence for the given key.
GetProof(storeKey []byte, version uint64, key []byte) ([]proof.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 []byte, version uint64, key []byte) ([]byte, error)

// SetInitialVersion sets the initial version of the committer.
SetInitialVersion(version uint64) error

// GetCommitInfo returns the CommitInfo for the given version.
GetCommitInfo(version uint64) (*proof.CommitInfo, error)

Get(storeKey []byte, version uint64, key []byte) ([]byte, error)

// Close releases associated resources. It should NOT be idempotent. It must
// only be called once and any call after may panic.
io.Closer
Expand Down
15 changes: 15 additions & 0 deletions store/v2/mock/db_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion store/v2/mock/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type StateCommitter interface {

// StateStorage is a mock of store.VersionedDatabase
type StateStorage interface {
store.VersionedDatabase
store.VersionedWriter
store.UpgradableDatabase
store.Pruner
store.PausablePruner
Expand Down
20 changes: 11 additions & 9 deletions store/v2/root/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type Store struct {
dbCloser io.Closer

// stateStorage reflects the state storage backend
stateStorage store.VersionedDatabase
stateStorage store.VersionedWriter

// stateCommitment reflects the state commitment (SC) backend
stateCommitment store.Committer
Expand Down Expand Up @@ -74,7 +74,7 @@ type Store struct {
func New(
dbCloser io.Closer,
logger corelog.Logger,
ss store.VersionedDatabase,
ss store.VersionedWriter,
sc store.Committer,
pm *pruning.Manager,
mm *migration.Manager,
Expand Down Expand Up @@ -127,19 +127,21 @@ func (s *Store) StateLatest() (uint64, corestore.ReaderMap, error) {
return v, NewReaderMap(v, s), nil
}

// StateAt checks if the requested version is present in ss.
func (s *Store) StateAt(v uint64) (corestore.ReaderMap, error) {
// 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/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)
// check if version is present in state storage
isExist, err := s.stateStorage.VersionExists(v)
if err != nil {
return nil, err
}
if !isExist {
return nil, fmt.Errorf("version %d does not exist", v)
}

return NewReaderMap(v, s), nil
}

func (s *Store) GetStateStorage() store.VersionedDatabase {
func (s *Store) GetStateStorage() store.VersionedWriter {
return s.stateStorage
}

Expand Down
2 changes: 1 addition & 1 deletion store/v2/root/store_mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
"cosmossdk.io/store/v2/pruning"
)

func newTestRootStore(ss store.VersionedDatabase, sc store.Committer) *Store {
func newTestRootStore(ss store.VersionedWriter, sc store.Committer) *Store {
noopLog := coretesting.NewNopLogger()
pm := pruning.NewManager(sc.(store.Pruner), ss.(store.Pruner), nil, nil)
return &Store{
Expand Down
2 changes: 1 addition & 1 deletion store/v2/root/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (s *RootStoreTestSuite) newStoreWithPruneConfig(config *store.PruningOption
s.rootStore = rs
}

func (s *RootStoreTestSuite) newStoreWithBackendMount(ss store.VersionedDatabase, sc store.Committer, pm *pruning.Manager) {
func (s *RootStoreTestSuite) newStoreWithBackendMount(ss store.VersionedWriter, sc store.Committer, pm *pruning.Manager) {
noopLog := coretesting.NewNopLogger()

rs, err := New(dbm.NewMemDB(), noopLog, ss, sc, pm, nil, nil)
Expand Down
1 change: 1 addition & 0 deletions store/v2/storage/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Database interface {
Get(storeKey []byte, version uint64, key []byte) ([]byte, error)
GetLatestVersion() (uint64, error)
SetLatestVersion(version uint64) error
VersionExists(version uint64) (bool, error)

Iterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error)
ReverseIterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error)
Expand Down
9 changes: 9 additions & 0 deletions store/v2/storage/pebbledb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ func (db *Database) GetLatestVersion() (uint64, error) {
return binary.LittleEndian.Uint64(bz), closer.Close()
}

func (db *Database) VersionExists(version uint64) (bool, error) {
latestVersion, err := db.GetLatestVersion()
if err != nil {
return false, err
}

return latestVersion >= version && version >= db.earliestVersion, nil
}

func (db *Database) setPruneHeight(pruneVersion uint64) error {
db.earliestVersion = pruneVersion + 1

Expand Down
9 changes: 9 additions & 0 deletions store/v2/storage/rocksdb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ func (db *Database) GetLatestVersion() (uint64, error) {
return binary.LittleEndian.Uint64(bz), nil
}

func (db *Database) VersionExists(version uint64) (bool, error) {
latestVersion, err := db.GetLatestVersion()
if err != nil {
return false, err
}

return latestVersion >= version && version >= db.tsLow, nil
}

func (db *Database) Has(storeKey []byte, version uint64, key []byte) (bool, error) {
slice, err := db.getSlice(storeKey, version, key)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions store/v2/storage/rocksdb/db_noflag.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ func (db *Database) GetLatestVersion() (uint64, error) {
panic("rocksdb requires a build flag")
}

func (db *Database) VersionExists(version uint64) (bool, error) {
panic("rocksdb requires a build flag")
}

func (db *Database) Has(storeKey []byte, version uint64, key []byte) (bool, error) {
panic("rocksdb requires a build flag")
}
Expand Down
15 changes: 14 additions & 1 deletion store/v2/storage/sqlite/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ func (db *Database) NewBatch(version uint64) (store.Batch, error) {
}

func (db *Database) GetLatestVersion() (uint64, error) {
stmt, err := db.storage.Prepare("SELECT value FROM state_storage WHERE store_key = ? AND key = ?")
stmt, err := db.storage.Prepare(`
SELECT value
FROM state_storage
WHERE store_key = ? AND key = ?
`)
if err != nil {
return 0, fmt.Errorf("failed to prepare SQL statement: %w", err)
}
Expand All @@ -123,6 +127,15 @@ func (db *Database) GetLatestVersion() (uint64, error) {
return latestHeight, nil
}

func (db *Database) VersionExists(v uint64) (bool, error) {
latestVersion, err := db.GetLatestVersion()
if err != nil {
return false, err
}

return latestVersion >= v && v >= db.earliestVersion, nil
}

func (db *Database) SetLatestVersion(version uint64) error {
_, err := db.storage.Exec(reservedUpsertStmt, reservedStoreKey, keyLatestHeight, version, 0, version)
if err != nil {
Expand Down
8 changes: 4 additions & 4 deletions store/v2/storage/storage_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ import (
var storeKey1 = []byte("store1")

var (
backends = map[string]func(dataDir string) (store.VersionedDatabase, error){
"rocksdb_versiondb_opts": func(dataDir string) (store.VersionedDatabase, error) {
backends = map[string]func(dataDir string) (store.VersionedWriter, error){
"rocksdb_versiondb_opts": func(dataDir string) (store.VersionedWriter, error) {
db, err := rocksdb.New(dataDir)
return storage.NewStorageStore(db, coretesting.NewNopLogger()), err
},
"pebbledb_default_opts": func(dataDir string) (store.VersionedDatabase, error) {
"pebbledb_default_opts": func(dataDir string) (store.VersionedWriter, error) {
db, err := pebbledb.New(dataDir)
if err == nil && db != nil {
db.SetSync(false)
}

return storage.NewStorageStore(db, coretesting.NewNopLogger()), err
},
"btree_sqlite": func(dataDir string) (store.VersionedDatabase, error) {
"btree_sqlite": func(dataDir string) (store.VersionedWriter, error) {
db, err := sqlite.New(dataDir)
return storage.NewStorageStore(db, coretesting.NewNopLogger()), err
},
Expand Down
Loading

0 comments on commit 76529d7

Please sign in to comment.