Merge PR #5579: Fix Restart application issue

This commit is contained in:
Aditya
2020-02-06 15:58:32 -05:00
committed by GitHub
parent 53bf2271d5
commit dba80caec0
10 changed files with 394 additions and 122 deletions
+43 -31
View File
@@ -28,13 +28,13 @@ const (
// cacheMultiStore which is for cache-wrapping other MultiStores. It implements
// the CommitMultiStore interface.
type Store struct {
db dbm.DB
lastCommitID types.CommitID
pruningOpts types.PruningOptions
storesParams map[types.StoreKey]storeParams
stores map[types.StoreKey]types.CommitKVStore
keysByName map[string]types.StoreKey
lazyLoading bool
db dbm.DB
lastCommitInfo commitInfo
pruningOpts types.PruningOptions
storesParams map[types.StoreKey]storeParams
stores map[types.StoreKey]types.CommitKVStore
keysByName map[string]types.StoreKey
lazyLoading bool
traceWriter io.Writer
traceContext types.TraceContext
@@ -146,11 +146,12 @@ func (rs *Store) LoadVersion(ver int64) error {
func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error {
infos := make(map[string]storeInfo)
var lastCommitID types.CommitID
var cInfo commitInfo
// load old data if we are not version 0
if ver != 0 {
cInfo, err := getCommitInfo(rs.db, ver)
var err error
cInfo, err = getCommitInfo(rs.db, ver)
if err != nil {
return err
}
@@ -159,7 +160,6 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error {
for _, storeInfo := range cInfo.StoreInfos {
infos[storeInfo.Name] = storeInfo
}
lastCommitID = cInfo.CommitID()
}
// load each Store (note this doesn't panic on unmounted keys now)
@@ -197,7 +197,7 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error {
}
}
rs.lastCommitID = lastCommitID
rs.lastCommitInfo = cInfo
rs.stores = newStores
return nil
@@ -281,29 +281,26 @@ func (rs *Store) TracingEnabled() bool {
// Implements Committer/CommitStore.
func (rs *Store) LastCommitID() types.CommitID {
return rs.lastCommitID
return rs.lastCommitInfo.CommitID()
}
// Implements Committer/CommitStore.
func (rs *Store) Commit() types.CommitID {
// Commit stores.
version := rs.lastCommitID.Version + 1
commitInfo := commitStores(version, rs.stores)
version := rs.lastCommitInfo.Version + 1
rs.lastCommitInfo = commitStores(version, rs.stores)
// Need to update atomically.
batch := rs.db.NewBatch()
defer batch.Close()
setCommitInfo(batch, version, commitInfo)
setLatestVersion(batch, version)
batch.Write()
// write CommitInfo to disk only if this version was flushed to disk
if rs.pruningOpts.FlushVersion(version) {
flushCommitInfo(rs.db, version, rs.lastCommitInfo)
}
// Prepare for next version.
commitID := types.CommitID{
Version: version,
Hash: commitInfo.Hash(),
Hash: rs.lastCommitInfo.Hash(),
}
rs.lastCommitID = commitID
return commitID
}
@@ -412,7 +409,6 @@ func (rs *Store) getStoreByName(name string) types.Store {
// Ie. `req.Path` here is `/<substore>/<path>`, and trimmed to `/<path>` for the substore.
// TODO: add proof for `multistore -> substore`.
func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery {
// Query just routes this to a substore.
path := req.Path
storeName, subpath, err := parsePath(path)
if err != nil {
@@ -441,9 +437,18 @@ func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery {
return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "proof is unexpectedly empty; ensure height has not been pruned"))
}
commitInfo, errMsg := getCommitInfo(rs.db, res.Height)
if errMsg != nil {
return sdkerrors.QueryResult(err)
// If the request's height is the latest height we've committed, then utilize
// the store's lastCommitInfo as this commit info may not be flushed to disk.
// Otherwise, we query for the commit info from disk.
var commitInfo commitInfo
if res.Height == rs.lastCommitInfo.Version {
commitInfo = rs.lastCommitInfo
} else {
commitInfo, err = getCommitInfo(rs.db, res.Height)
if err != nil {
return sdkerrors.QueryResult(err)
}
}
// Restore origin path and append proof op.
@@ -626,26 +631,22 @@ func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore
storeInfos := make([]storeInfo, 0, len(storeMap))
for key, store := range storeMap {
// Commit
commitID := store.Commit()
if store.GetStoreType() == types.StoreTypeTransient {
continue
}
// Record CommitID
si := storeInfo{}
si.Name = key.Name()
si.Core.CommitID = commitID
// si.Core.StoreType = store.GetStoreType()
storeInfos = append(storeInfos, si)
}
ci := commitInfo{
return commitInfo{
Version: version,
StoreInfos: storeInfos,
}
return ci
}
// Gets commitInfo from disk.
@@ -676,3 +677,14 @@ func setCommitInfo(batch dbm.Batch, version int64, cInfo commitInfo) {
cInfoKey := fmt.Sprintf(commitInfoKeyFmt, version)
batch.Set([]byte(cInfoKey), cInfoBytes)
}
// flushCommitInfo flushes a commitInfo for given version to the DB. Note, this
// needs to happen atomically.
func flushCommitInfo(db dbm.DB, version int64, cInfo commitInfo) {
batch := db.NewBatch()
defer batch.Close()
setCommitInfo(batch, version, cInfo)
setLatestVersion(batch, version)
batch.Write()
}
+88 -5
View File
@@ -1,6 +1,7 @@
package rootmulti
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
@@ -53,7 +54,7 @@ func TestStoreMount(t *testing.T) {
func TestCacheMultiStoreWithVersion(t *testing.T) {
var db dbm.DB = dbm.NewMemDB()
ms := newMultiStoreWithMounts(db, types.PruneSyncable)
ms := newMultiStoreWithMounts(db, types.PruneNothing)
err := ms.LoadLatestVersion()
require.Nil(t, err)
@@ -90,7 +91,7 @@ func TestCacheMultiStoreWithVersion(t *testing.T) {
func TestHashStableWithEmptyCommit(t *testing.T) {
var db dbm.DB = dbm.NewMemDB()
ms := newMultiStoreWithMounts(db, types.PruneSyncable)
ms := newMultiStoreWithMounts(db, types.PruneNothing)
err := ms.LoadLatestVersion()
require.Nil(t, err)
@@ -114,7 +115,7 @@ func TestHashStableWithEmptyCommit(t *testing.T) {
func TestMultistoreCommitLoad(t *testing.T) {
var db dbm.DB = dbm.NewMemDB()
store := newMultiStoreWithMounts(db, types.PruneSyncable)
store := newMultiStoreWithMounts(db, types.PruneNothing)
err := store.LoadLatestVersion()
require.Nil(t, err)
@@ -139,7 +140,7 @@ func TestMultistoreCommitLoad(t *testing.T) {
}
// Load the latest multistore again and check version.
store = newMultiStoreWithMounts(db, types.PruneSyncable)
store = newMultiStoreWithMounts(db, types.PruneNothing)
err = store.LoadLatestVersion()
require.Nil(t, err)
commitID = getExpectedCommitID(store, nCommits)
@@ -152,7 +153,7 @@ func TestMultistoreCommitLoad(t *testing.T) {
// Load an older multistore and check version.
ver := nCommits - 1
store = newMultiStoreWithMounts(db, types.PruneSyncable)
store = newMultiStoreWithMounts(db, types.PruneNothing)
err = store.LoadVersion(ver)
require.Nil(t, err)
commitID = getExpectedCommitID(store, ver)
@@ -289,6 +290,88 @@ func TestParsePath(t *testing.T) {
}
func TestMultiStoreRestart(t *testing.T) {
db := dbm.NewMemDB()
pruning := types.PruningOptions{
KeepEvery: 3,
SnapshotEvery: 6,
}
multi := newMultiStoreWithMounts(db, pruning)
err := multi.LoadLatestVersion()
require.Nil(t, err)
initCid := multi.LastCommitID()
k, v := "wind", "blows"
k2, v2 := "water", "flows"
k3, v3 := "fire", "burns"
for i := 1; i < 3; i++ {
// Set and commit data in one store.
store1 := multi.getStoreByName("store1").(types.KVStore)
store1.Set([]byte(k), []byte(fmt.Sprintf("%s:%d", v, i)))
// ... and another.
store2 := multi.getStoreByName("store2").(types.KVStore)
store2.Set([]byte(k2), []byte(fmt.Sprintf("%s:%d", v2, i)))
// ... and another.
store3 := multi.getStoreByName("store3").(types.KVStore)
store3.Set([]byte(k3), []byte(fmt.Sprintf("%s:%d", v3, i)))
multi.Commit()
cinfo, err := getCommitInfo(multi.db, int64(i))
require.NotNil(t, err)
require.Equal(t, commitInfo{}, cinfo)
}
// Set and commit data in one store.
store1 := multi.getStoreByName("store1").(types.KVStore)
store1.Set([]byte(k), []byte(fmt.Sprintf("%s:%d", v, 3)))
// ... and another.
store2 := multi.getStoreByName("store2").(types.KVStore)
store2.Set([]byte(k2), []byte(fmt.Sprintf("%s:%d", v2, 3)))
multi.Commit()
flushedCinfo, err := getCommitInfo(multi.db, 3)
require.Nil(t, err)
require.NotEqual(t, initCid, flushedCinfo, "CID is different after flush to disk")
// ... and another.
store3 := multi.getStoreByName("store3").(types.KVStore)
store3.Set([]byte(k3), []byte(fmt.Sprintf("%s:%d", v3, 3)))
multi.Commit()
postFlushCinfo, err := getCommitInfo(multi.db, 4)
require.NotNil(t, err)
require.Equal(t, commitInfo{}, postFlushCinfo, "Commit changed after in-memory commit")
multi = newMultiStoreWithMounts(db, pruning)
err = multi.LoadLatestVersion()
require.Nil(t, err)
reloadedCid := multi.LastCommitID()
require.Equal(t, flushedCinfo.CommitID(), reloadedCid, "Reloaded CID is not the same as last flushed CID")
// Check that store1 and store2 retained date from 3rd commit
store1 = multi.getStoreByName("store1").(types.KVStore)
val := store1.Get([]byte(k))
require.Equal(t, []byte(fmt.Sprintf("%s:%d", v, 3)), val, "Reloaded value not the same as last flushed value")
store2 = multi.getStoreByName("store2").(types.KVStore)
val2 := store2.Get([]byte(k2))
require.Equal(t, []byte(fmt.Sprintf("%s:%d", v2, 3)), val2, "Reloaded value not the same as last flushed value")
// Check that store3 still has data from last commit even though update happened on 2nd commit
store3 = multi.getStoreByName("store3").(types.KVStore)
val3 := store3.Get([]byte(k3))
require.Equal(t, []byte(fmt.Sprintf("%s:%d", v3, 2)), val3, "Reloaded value not the same as last flushed value")
}
func TestMultiStoreQuery(t *testing.T) {
db := dbm.NewMemDB()
multi := newMultiStoreWithMounts(db, types.PruneNothing)