Merge PR #4059: Add support for graceful halt via server config

This commit is contained in:
Alexander Bezobchuk 2019-04-23 09:33:11 -04:00 committed by GitHub
parent 4bf9d2b3ec
commit c6cb84c558
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 58 additions and 4 deletions

View File

@ -0,0 +1,2 @@
#3981 Add support to gracefully halt a node at a given height
via the node's `halt-height` config or CLI value.

View File

@ -3,6 +3,7 @@ package baseapp
import (
"fmt"
"io"
"os"
"reflect"
"runtime/debug"
"strings"
@ -81,6 +82,9 @@ type BaseApp struct {
// flag for sealing options and parameters to a BaseApp
sealed bool
// height at which to halt the chain and gracefully shutdown
haltHeight uint64
}
var _ abci.Application = (*BaseApp)(nil)
@ -230,6 +234,10 @@ func (app *BaseApp) setMinGasPrices(gasPrices sdk.DecCoins) {
app.minGasPrices = gasPrices
}
func (app *BaseApp) setHaltHeight(height uint64) {
app.haltHeight = height
}
// Router returns the router of the BaseApp.
func (app *BaseApp) Router() Router {
if app.sealed {
@ -885,7 +893,13 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc
return
}
// Commit implements the ABCI interface.
// Commit implements the ABCI interface. It will commit all state that exists in
// the deliver state's multi-store and includes the resulting commit ID in the
// returned abci.ResponseCommit. Commit will set the check state based on the
// latest header and reset the deliver state. Also, if a non-zero halt height is
// defined in config, Commit will execute a deferred function call to check
// against that height and gracefully halt if it matches the latest committed
// height.
func (app *BaseApp) Commit() (res abci.ResponseCommit) {
header := app.deliverState.ctx.BlockHeader()
@ -896,13 +910,20 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) {
// Reset the Check state to the latest committed.
//
// NOTE: safe because Tendermint holds a lock on the mempool for Commit.
// Use the header from this latest block.
// NOTE: This is safe because Tendermint holds a lock on the mempool for
// Commit. Use the header from this latest block.
app.setCheckState(header)
// empty/reset the deliver state
app.deliverState = nil
defer func() {
if app.haltHeight > 0 && uint64(header.Height) == app.haltHeight {
app.logger.Info("halting node per configuration", "height", app.haltHeight)
os.Exit(0)
}
}()
return abci.ResponseCommit{
Data: commitID.Hash,
}

View File

@ -28,6 +28,11 @@ func SetMinGasPrices(gasPricesStr string) func(*BaseApp) {
return func(bap *BaseApp) { bap.setMinGasPrices(gasPrices) }
}
// SetHaltHeight returns a BaseApp option function that sets the halt height.
func SetHaltHeight(height uint64) func(*BaseApp) {
return func(bap *BaseApp) { bap.setHaltHeight(height) }
}
func (app *BaseApp) SetName(name string) {
if app.sealed {
panic("SetName() on sealed BaseApp")

View File

@ -70,6 +70,7 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application
logger, db, traceStore, true, invCheckPeriod,
baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))),
baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)),
baseapp.SetHaltHeight(uint64(viper.GetInt(server.FlagHaltHeight))),
)
}

View File

@ -236,6 +236,8 @@ func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error {
return err
}
// TODO: Rename config file to server.toml as it's not particular to Gaia
// (REF: https://github.com/cosmos/cosmos-sdk/issues/4125).
gaiaConfigFilePath := filepath.Join(nodeDir, "config/gaiad.toml")
srvconfig.WriteConfigFile(gaiaConfigFilePath, gaiaConfig)
}

View File

@ -165,6 +165,15 @@ You should also be able to see your validator on the [Explorer](https://explorec
To be in the validator set, you need to have more total voting power than the 100th validator.
:::
## Halting Your Validator
When attempting to perform routine maintenance or planning for an upcoming coordinated
upgrade, it can be useful to have your validator systematically and gracefully halt.
You can achieve this by either setting the `halt-height` to the height at which
you want your node to shutdown or by passing the `--halt-height` flag to `gaiad`.
The node will shutdown with a zero exit code at that given height after committing
the block.
## Common Problems
### Problem #1: My validator has `voting_power: 0`

View File

@ -17,6 +17,10 @@ type BaseConfig struct {
// transaction. A transaction's fees must meet the minimum of any denomination
// specified in this config (e.g. 0.25token1;0.0001token2).
MinGasPrices string `mapstructure:"minimum-gas-prices"`
// HaltHeight contains a non-zero height at which a node will gracefully halt
// and shutdown that can be used to assist upgrades and testing.
HaltHeight uint64 `mapstructure:"halt-height"`
}
// Config defines the server's top level configuration
@ -56,6 +60,7 @@ func DefaultConfig() *Config {
return &Config{
BaseConfig{
MinGasPrices: defaultMinGasPrices,
HaltHeight: 0,
},
}
}

View File

@ -17,6 +17,10 @@ const defaultConfigTemplate = `# This is a TOML config file.
# transaction. A transaction's fees must meet the minimum of any denomination
# specified in this config (e.g. 0.25token1;0.0001token2).
minimum-gas-prices = "{{ .BaseConfig.MinGasPrices }}"
# HaltHeight contains a non-zero height at which a node will gracefully halt
# and shutdown that can be used to assist upgrades and testing.
halt-height = {{ .BaseConfig.HaltHeight }}
`
var configTemplate *template.Template

View File

@ -23,6 +23,7 @@ const (
flagTraceStore = "trace-store"
flagPruning = "pruning"
FlagMinGasPrices = "minimum-gas-prices"
FlagHaltHeight = "halt-height"
)
// StartCmd runs the service passed in, either stand-alone or in-process with
@ -53,6 +54,7 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command {
FlagMinGasPrices, "",
"Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)",
)
cmd.Flags().Uint64(FlagHaltHeight, 0, "Height at which to gracefully halt the chain and shutdown the node")
// add support for all Tendermint-specific command line options
tcmd.AddNodeFlags(cmd)

View File

@ -105,7 +105,10 @@ func interceptLoadConfig() (conf *cfg.Config, err error) {
conf, err = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary.
}
// create a default gaia config file if it does not exist
// create a default Gaia config file if it does not exist
//
// TODO: Rename config file to server.toml as it's not particular to Gaia
// (REF: https://github.com/cosmos/cosmos-sdk/issues/4125).
gaiaConfigFilePath := filepath.Join(rootDir, "config/gaiad.toml")
if _, err := os.Stat(gaiaConfigFilePath); os.IsNotExist(err) {
gaiaConf, _ := config.ParseConfig()