Merge PR #5005: Add support for halt-time

This commit is contained in:
Alexander Bezobchuk 2019-09-09 10:08:10 -04:00 committed by GitHub
parent 2c4faf983b
commit c8d84b4df4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 119 additions and 20 deletions

View File

@ -57,6 +57,9 @@ via the `--cpu-profile` flag.
* (store) [\#4724](https://github.com/cosmos/cosmos-sdk/issues/4724) Multistore supports substore migrations upon load. New `rootmulti.Store.LoadLatestVersionAndUpgrade` method in
`Baseapp` supports `StoreLoader` to enable various upgrade strategies. It no
longer panics if the store to load contains substores that we didn't explicitly mount.
* [\#4979](https://github.com/cosmos/cosmos-sdk/issues/4979) Introduce a new `halt-time` config and
CLI option to the `start` command. When provided, an application will halt during `Commit` when the
block time is >= the `halt-time`.
### Improvements
@ -90,6 +93,8 @@ to detail this new feature and how state transitions occur.
* (cli) [\#4763](https://github.com/cosmos/cosmos-sdk/issues/4763) Fix flag `--min-self-delegation` for staking `EditValidator`
* (keys) Fix ledger custom coin type support bug
* [\#4979](https://github.com/cosmos/cosmos-sdk/issues/4979) Use `Signal(os.Interrupt)` over
`os.Exit(0)` during configured halting to allow any `defer` calls to be executed.
## [v0.37.0] - 2019-08-21

View File

@ -5,6 +5,7 @@ import (
"os"
"sort"
"strings"
"syscall"
abci "github.com/tendermint/tendermint/abci/types"
@ -214,6 +215,24 @@ func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) (res abci.ResponseDeliv
func (app *BaseApp) Commit() (res abci.ResponseCommit) {
header := app.deliverState.ctx.BlockHeader()
var halt bool
switch {
case app.haltHeight > 0 && uint64(header.Height) >= app.haltHeight:
halt = true
case app.haltTime > 0 && header.Time.Unix() >= int64(app.haltTime):
halt = true
}
if halt {
app.halt()
// Note: State is not actually committed when halted. Logs from Tendermint
// can be ignored.
return abci.ResponseCommit{}
}
// Write the DeliverTx state which is cache-wrapped and commit the MultiStore.
// The write to the DeliverTx state writes all state transitions to the root
// MultiStore (app.cms) so when Commit() is called is persists those values.
@ -230,18 +249,33 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) {
// 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,
}
}
// halt attempts to gracefully shutdown the node via SIGINT and SIGTERM falling
// back on os.Exit if both fail.
func (app *BaseApp) halt() {
app.logger.Info("halting node per configuration", "height", app.haltHeight, "time", app.haltTime)
p, err := os.FindProcess(os.Getpid())
if err == nil {
// attempt cascading signals in case SIGINT fails (os dependent)
sigIntErr := p.Signal(syscall.SIGINT)
sigTermErr := p.Signal(syscall.SIGTERM)
if sigIntErr == nil || sigTermErr == nil {
return
}
}
// Resort to exiting immediately if the process could not be found or killed
// via SIGINT/SIGTERM signals.
app.logger.Info("failed to send SIGINT/SIGTERM; exiting...")
os.Exit(0)
}
// Query implements the ABCI interface. It delegates to CommitMultiStore if it
// implements Queryable.
func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {

View File

@ -100,9 +100,12 @@ type BaseApp struct {
// flag for sealing options and parameters to a BaseApp
sealed bool
// height at which to halt the chain and gracefully shutdown
// block height at which to halt the chain and gracefully shutdown
haltHeight uint64
// minimum block time (in Unix seconds) at which to halt the chain and gracefully shutdown
haltTime uint64
// application's version string
appVersion string
}
@ -341,8 +344,12 @@ func (app *BaseApp) setMinGasPrices(gasPrices sdk.DecCoins) {
app.minGasPrices = gasPrices
}
func (app *BaseApp) setHaltHeight(height uint64) {
app.haltHeight = height
func (app *BaseApp) setHaltHeight(haltHeight uint64) {
app.haltHeight = haltHeight
}
func (app *BaseApp) setHaltTime(haltTime uint64) {
app.haltTime = haltTime
}
func (app *BaseApp) setInterBlockCache(cache sdk.MultiStorePersistentCache) {

View File

@ -29,9 +29,14 @@ 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) }
// SetHaltHeight returns a BaseApp option function that sets the halt block height.
func SetHaltHeight(blockHeight uint64) func(*BaseApp) {
return func(bap *BaseApp) { bap.setHaltHeight(blockHeight) }
}
// SetHaltTime returns a BaseApp option function that sets the halt block time.
func SetHaltTime(haltTime uint64) func(*BaseApp) {
return func(bap *BaseApp) { bap.setHaltTime(haltTime) }
}
// SetInterBlockCache provides a BaseApp option function that sets the

View File

@ -18,9 +18,23 @@ type BaseConfig struct {
// 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 contains a non-zero block height at which a node will gracefully
// halt and shutdown that can be used to assist upgrades and testing.
//
// Note: State will not be committed on the corresponding height and any logs
// indicating such can be safely ignored.
HaltHeight uint64 `mapstructure:"halt-height"`
// HaltTime contains a non-zero minimum block time (in Unix seconds) at which
// a node will gracefully halt and shutdown that can be used to assist
// upgrades and testing.
//
// Note: State will not be committed on the corresponding height and any logs
// indicating such can be safely ignored.
HaltTime uint64 `mapstructure:"halt-time"`
// InterBlockCache enables inter-block caching.
InterBlockCache bool `mapstructure:"inter-block-cache"`
}
// Config defines the server's top level configuration
@ -59,8 +73,8 @@ func (c *Config) GetMinGasPrices() sdk.DecCoins {
func DefaultConfig() *Config {
return &Config{
BaseConfig{
MinGasPrices: defaultMinGasPrices,
HaltHeight: 0,
MinGasPrices: defaultMinGasPrices,
InterBlockCache: true,
},
}
}

View File

@ -18,9 +18,23 @@ const defaultConfigTemplate = `# This is a TOML config file.
# 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.
# HaltHeight contains a non-zero block height at which a node will gracefully
# halt and shutdown that can be used to assist upgrades and testing.
#
# Note: State will not be committed on the corresponding height and any logs
# indicating such can be safely ignored.
halt-height = {{ .BaseConfig.HaltHeight }}
# HaltTime contains a non-zero minimum block time (in Unix seconds) at which
# a node will gracefully halt and shutdown that can be used to assist upgrades
# and testing.
#
# Note: State will not be committed on the corresponding height and any logs
# indicating such can be safely ignored.
halt-time = {{ .BaseConfig.HaltTime }}
# InterBlockCache enables inter-block caching.
inter-block-cache = {{ .BaseConfig.InterBlockCache }}
`
var configTemplate *template.Template

View File

@ -27,6 +27,7 @@ const (
flagCPUProfile = "cpu-profile"
FlagMinGasPrices = "minimum-gas-prices"
FlagHaltHeight = "halt-height"
FlagHaltTime = "halt-time"
FlagInterBlockCache = "inter-block-cache"
)
@ -36,6 +37,24 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command {
cmd := &cobra.Command{
Use: "start",
Short: "Run the full node",
Long: `Run the full node application with Tendermint in or out of process. By
default, the application will run with Tendermint in process.
Pruning options can be provided via the '--pruning' flag. The options are as follows:
syncable: only those states not needed for state syncing will be deleted (keeps last 100 + every 10000th)
nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node)
everything: all saved states will be deleted, storing only the current state
Node halting configurations exist in the form of two flags: '--halt-height' and '--halt-time'. During
the ABCI Commit phase, the node will check if the current block height is greater than or equal to
the halt-height or if the current block time is greater than or equal to the halt-time. If so, the
node will attempt to gracefully shutdown and the block will not be committed. In addition, the node
will not be able to commit subsequent blocks.
For profiling and benchmarking purposes, CPU profiling can be enabled via the '--cpu-profile' flag
which accepts a path for the resulting pprof file.
`,
RunE: func(cmd *cobra.Command, args []string) error {
if !viper.GetBool(flagWithTendermint) {
ctx.Logger.Info("starting ABCI without Tendermint")
@ -58,7 +77,8 @@ 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")
cmd.Flags().Uint64(FlagHaltHeight, 0, "Block height at which to gracefully halt the chain and shutdown the node")
cmd.Flags().Uint64(FlagHaltTime, 0, "Minimum block time (in Unix seconds) at which to gracefully halt the chain and shutdown the node")
cmd.Flags().Bool(FlagInterBlockCache, true, "Enable inter-block caching")
cmd.Flags().String(flagCPUProfile, "", "Enable CPU profiling and write to the provided file")