Merge PR #4059: Add support for graceful halt via server config
This commit is contained in:
parent
4bf9d2b3ec
commit
c6cb84c558
|
@ -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.
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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))),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue