From d5cf7831f13cf881327976e53406be9dabb8f61c Mon Sep 17 00:00:00 2001 From: githubsands <26752232+githubsands@users.noreply.github.com> Date: Tue, 23 Feb 2021 08:37:46 -0700 Subject: [PATCH] state: save in batches within the state store (#6067) --- CHANGELOG_PENDING.md | 1 + state/store.go | 62 +++++++++++++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index b84243302..f177caad1 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -66,6 +66,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [types] \#5994 Reduce the use of protobuf types in core logic. (@marbar3778) - `ConsensusParams`, `BlockParams`, `ValidatorParams`, `EvidenceParams`, `VersionParams`, `sm.Version` and `version.Consensus` have become native types. They still utilize protobuf when being sent over the wire or written to disk. - [node] \#6059 Validate and complete genesis doc before saving to state store (@silasdavis) +- [state] \#6067 Batch save state data (@githubsands & @cmwaters) ### BUG FIXES diff --git a/state/store.go b/state/store.go index 4829a8b75..a488d0722 100644 --- a/state/store.go +++ b/state/store.go @@ -186,31 +186,36 @@ func (store dbStore) Save(state State) error { } func (store dbStore) save(state State, key []byte) error { + batch := store.db.NewBatch() + defer batch.Close() + nextHeight := state.LastBlockHeight + 1 // If first block, save validators for the block. if nextHeight == 1 { nextHeight = state.InitialHeight // This extra logic due to Tendermint validator set changes being delayed 1 block. // It may get overwritten due to InitChain validator updates. - if err := store.saveValidatorsInfo(nextHeight, nextHeight, state.Validators); err != nil { + if err := store.saveValidatorsInfo(nextHeight, nextHeight, state.Validators, batch); err != nil { return err } } // Save next validators. - if err := store.saveValidatorsInfo(nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators); err != nil { + err := store.saveValidatorsInfo(nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators, batch) + if err != nil { return err } // Save next consensus params. if err := store.saveConsensusParamsInfo(nextHeight, - state.LastHeightConsensusParamsChanged, state.ConsensusParams); err != nil { + state.LastHeightConsensusParamsChanged, state.ConsensusParams, batch); err != nil { return err } - err := store.db.SetSync(key, state.Bytes()) - if err != nil { + + if err := batch.Set(key, state.Bytes()); err != nil { return err } - return nil + + return batch.WriteSync() } // BootstrapState saves a new state, used e.g. by state sync when starting from non-zero height. @@ -220,26 +225,33 @@ func (store dbStore) Bootstrap(state State) error { height = state.InitialHeight } + batch := store.db.NewBatch() + defer batch.Close() + if height > 1 && !state.LastValidators.IsNilOrEmpty() { - if err := store.saveValidatorsInfo(height-1, height-1, state.LastValidators); err != nil { + if err := store.saveValidatorsInfo(height-1, height-1, state.LastValidators, batch); err != nil { return err } } - if err := store.saveValidatorsInfo(height, height, state.Validators); err != nil { + if err := store.saveValidatorsInfo(height, height, state.Validators, batch); err != nil { return err } - if err := store.saveValidatorsInfo(height+1, height+1, state.NextValidators); err != nil { + if err := store.saveValidatorsInfo(height+1, height+1, state.NextValidators, batch); err != nil { return err } if err := store.saveConsensusParamsInfo(height, - state.LastHeightConsensusParamsChanged, state.ConsensusParams); err != nil { + state.LastHeightConsensusParamsChanged, state.ConsensusParams, batch); err != nil { return err } - return store.db.SetSync(stateKey, state.Bytes()) + if err := batch.Set(stateKey, state.Bytes()); err != nil { + return err + } + + return batch.WriteSync() } // PruneStates deletes states up to the height specified (exclusive). It is not @@ -468,6 +480,10 @@ func (store dbStore) LoadABCIResponses(height int64) (*tmstate.ABCIResponses, er // // Exposed for testing. func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCIResponses) error { + return store.saveABCIResponses(height, abciResponses) +} + +func (store dbStore) saveABCIResponses(height int64, abciResponses *tmstate.ABCIResponses) error { var dtxs []*abci.ResponseDeliverTx // strip nil values, for _, tx := range abciResponses.DeliverTxs { @@ -475,6 +491,7 @@ func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCI dtxs = append(dtxs, tx) } } + abciResponses.DeliverTxs = dtxs bz, err := abciResponses.Marshal() @@ -482,12 +499,7 @@ func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCI return err } - err = store.db.SetSync(abciResponsesKey(height), bz) - if err != nil { - return err - } - - return nil + return store.db.SetSync(abciResponsesKey(height), bz) } //----------------------------------------------------------------------------- @@ -568,7 +580,11 @@ func loadValidatorsInfo(db dbm.DB, height int64) (*tmstate.ValidatorsInfo, error // `height` is the effective height for which the validator is responsible for // signing. It should be called from s.Save(), right before the state itself is // persisted. -func (store dbStore) saveValidatorsInfo(height, lastHeightChanged int64, valSet *types.ValidatorSet) error { +func (store dbStore) saveValidatorsInfo( + height, lastHeightChanged int64, + valSet *types.ValidatorSet, + batch dbm.Batch, +) error { if lastHeightChanged > height { return errors.New("lastHeightChanged cannot be greater than ValidatorsInfo height") } @@ -590,7 +606,7 @@ func (store dbStore) saveValidatorsInfo(height, lastHeightChanged int64, valSet return err } - err = store.db.Set(validatorsKey(height), bz) + err = batch.Set(validatorsKey(height), bz) if err != nil { return err } @@ -656,7 +672,11 @@ func (store dbStore) loadConsensusParamsInfo(height int64) (*tmstate.ConsensusPa // It should be called from s.Save(), right before the state itself is persisted. // If the consensus params did not change after processing the latest block, // only the last height for which they changed is persisted. -func (store dbStore) saveConsensusParamsInfo(nextHeight, changeHeight int64, params types.ConsensusParams) error { +func (store dbStore) saveConsensusParamsInfo( + nextHeight, changeHeight int64, + params types.ConsensusParams, + batch dbm.Batch, +) error { paramsInfo := &tmstate.ConsensusParamsInfo{ LastHeightChanged: changeHeight, } @@ -669,7 +689,7 @@ func (store dbStore) saveConsensusParamsInfo(nextHeight, changeHeight int64, par return err } - err = store.db.Set(consensusParamsKey(nextHeight), bz) + err = batch.Set(consensusParamsKey(nextHeight), bz) if err != nil { return err }