From 76b1cce39127f6d22f8e61385522a25f52b5e6b3 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 7 Dec 2023 06:17:50 +0400 Subject: [PATCH] perf(state): batch save `State` (#1735) * done stateDB writes batching * remove forgotten debug print * remove forgotten comments * format code * add a changelog entry * fix changelog --------- Co-authored-by: werty144 --- .../1715-validate-validator-address.md | 1 + .../improvements/1735-batch-save-state.md | 1 + internal/state/export_test.go | 11 +++- internal/state/store.go | 54 +++++++++++++------ 4 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 .changelog/unreleased/improvements/1715-validate-validator-address.md create mode 100644 .changelog/unreleased/improvements/1735-batch-save-state.md diff --git a/.changelog/unreleased/improvements/1715-validate-validator-address.md b/.changelog/unreleased/improvements/1715-validate-validator-address.md new file mode 100644 index 0000000000..ec7f2c7da6 --- /dev/null +++ b/.changelog/unreleased/improvements/1715-validate-validator-address.md @@ -0,0 +1 @@ +- `[types]` Validate `Validator#Address` in `ValidateBasic` ([\#1715](https://github.com/cometbft/cometbft/pull/1715)) diff --git a/.changelog/unreleased/improvements/1735-batch-save-state.md b/.changelog/unreleased/improvements/1735-batch-save-state.md new file mode 100644 index 0000000000..721380f604 --- /dev/null +++ b/.changelog/unreleased/improvements/1735-batch-save-state.md @@ -0,0 +1 @@ +- `[state]` Save the state using a single DB batch ([\#1735](https://github.com/cometbft/cometbft/pull/1735)) diff --git a/internal/state/export_test.go b/internal/state/export_test.go index 1914644f8c..88e7fe9ee6 100644 --- a/internal/state/export_test.go +++ b/internal/state/export_test.go @@ -42,7 +42,16 @@ func ValidateValidatorUpdates(abciUpdates []abci.ValidatorUpdate, params types.V // store.go, exported exclusively and explicitly for testing. func SaveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) error { stateStore := dbStore{db, StoreOptions{DiscardABCIResponses: false}} - return stateStore.saveValidatorsInfo(height, lastHeightChanged, valSet) + batch := stateStore.db.NewBatch() + err := stateStore.saveValidatorsInfo(height, lastHeightChanged, valSet, batch) + if err != nil { + return err + } + err = batch.WriteSync() + if err != nil { + return err + } + return nil } // FindMinBlockRetainHeight is an alias for the private diff --git a/internal/state/store.go b/internal/state/store.go index 7585612c31..a0f5626ced 100644 --- a/internal/state/store.go +++ b/internal/state/store.go @@ -209,61 +209,83 @@ func (store dbStore) Save(state State) error { } func (store dbStore) save(state State, key []byte) error { + batch := store.db.NewBatch() + defer func(batch dbm.Batch) { + err := batch.Close() + if err != nil { + panic(err) + } + }(batch) nextHeight := state.LastBlockHeight + 1 // If first block, save validators for the block. if nextHeight == 1 { nextHeight = state.InitialHeight // This extra logic due to 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 { + if err := store.saveValidatorsInfo(nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators, batch); 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 } - + if err := batch.WriteSync(); err != nil { + panic(err) + } return nil } // BootstrapState saves a new state, used e.g. by state sync when starting from non-zero height. func (store dbStore) Bootstrap(state State) error { + batch := store.db.NewBatch() + defer func(batch dbm.Batch) { + err := batch.Close() + if err != nil { + panic(err) + } + }(batch) height := state.LastBlockHeight + 1 if height == 1 { height = state.InitialHeight } 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 + } + + if err := batch.WriteSync(); err != nil { + panic(err) + } + + return batch.Close() } // PruneStates deletes states between the given heights (including from, excluding to). It is not @@ -742,7 +764,7 @@ func loadValidatorsInfo(db dbm.DB, height int64) (*cmtstate.ValidatorsInfo, erro // `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") } @@ -764,7 +786,7 @@ func (store dbStore) saveValidatorsInfo(height, lastHeightChanged int64, valSet return err } - err = store.db.Set(calcValidatorsKey(height), bz) + err = batch.Set(calcValidatorsKey(height), bz) if err != nil { return err } @@ -828,7 +850,7 @@ func (store dbStore) loadConsensusParamsInfo(height int64) (*cmtstate.ConsensusP // 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 := &cmtstate.ConsensusParamsInfo{ LastHeightChanged: changeHeight, } @@ -841,7 +863,7 @@ func (store dbStore) saveConsensusParamsInfo(nextHeight, changeHeight int64, par return err } - err = store.db.Set(calcConsensusParamsKey(nextHeight), bz) + err = batch.Set(calcConsensusParamsKey(nextHeight), bz) if err != nil { return err }