Skip to content

Commit

Permalink
Merge PR #5005: Add support for halt-time
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderbez committed Sep 17, 2019
1 parent 733f734 commit 816afbf
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 20 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,21 @@ Ref: https://keepachangelog.com/en/1.0.0/

* (cli) [\#4973](https://github.com/cosmos/cosmos-sdk/pull/4973) Enable application CPU profiling
via the `--cpu-profile` flag.
* [\#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

* [\#4990](https://github.com/cosmos/cosmos-sdk/issues/4990) Add `Events` to the `ABCIMessageLog` to
provide context and grouping of events based on the messages they correspond to. The `Events` field
in `TxResponse` is deprecated and will be removed in the next major release.

### Bug Fixes

* [\#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

### Bug Fixes
Expand Down
65 changes: 54 additions & 11 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"runtime/debug"
"sort"
"strings"
"syscall"

"errors"

Expand Down Expand Up @@ -83,9 +84,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
}
Expand Down Expand Up @@ -264,8 +268,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
}

// Router returns the router of the BaseApp.
Expand Down Expand Up @@ -983,7 +991,27 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc
func (app *BaseApp) Commit() (res abci.ResponseCommit) {
header := app.deliverState.ctx.BlockHeader()

// write the Deliver state and commit the MultiStore
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.
app.deliverState.ms.Write()
commitID := app.cms.Commit()
app.logger.Debug("Commit synced", "commit", fmt.Sprintf("%X", commitID))
Expand All @@ -997,18 +1025,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)
}

// ----------------------------------------------------------------------------
// State

Expand Down
11 changes: 8 additions & 3 deletions baseapp/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,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) }
}

func (app *BaseApp) SetName(name string) {
Expand Down
22 changes: 18 additions & 4 deletions server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -59,8 +73,8 @@ func (c *Config) GetMinGasPrices() sdk.DecCoins {
func DefaultConfig() *Config {
return &Config{
BaseConfig{
MinGasPrices: defaultMinGasPrices,
HaltHeight: 0,
MinGasPrices: defaultMinGasPrices,
InterBlockCache: true,
},
}
}
18 changes: 16 additions & 2 deletions server/config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions server/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (
flagCPUProfile = "cpu-profile"
FlagMinGasPrices = "minimum-gas-prices"
FlagHaltHeight = "halt-height"
FlagHaltTime = "halt-time"
)

// StartCmd runs the service passed in, either stand-alone or in-process with
Expand All @@ -35,6 +36,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")
Expand All @@ -58,6 +77,7 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command {
"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(FlagHaltTime, 0, "Minimum block time (in Unix seconds) at which to gracefully halt the chain and shutdown the node")
cmd.Flags().String(flagCPUProfile, "", "Enable CPU profiling and write to the provided file")

// add support for all Tendermint-specific command line options
Expand Down

0 comments on commit 816afbf

Please sign in to comment.