Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for halt-time #5005

Merged
merged 12 commits into from
Sep 9, 2019
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
46 changes: 39 additions & 7 deletions baseapp/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"sort"
"strings"
"syscall"

abci "github.com/tendermint/tendermint/abci/types"

Expand Down Expand Up @@ -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.UnixNano() >= int64(app.haltTime):
halt = true
}

if halt {
app.halt()

// Note: State is not actually committed when halted. Logs from Tendermint
// can be ignored.
return
}

// 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.
Expand All @@ -230,18 +249,31 @@ 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,
}
}

func (app *BaseApp) halt() {
app.logger.Info("halting node per configuration", "height", app.haltHeight, "time", app.haltTime)
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved

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)
tnachen marked this conversation as resolved.
Show resolved Hide resolved

if sigIntErr == nil || sigTermErr == nil {
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
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) {
Expand Down
13 changes: 10 additions & 3 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 at which to halt the chain and gracefully shutdown
haltTime uint64

// application's version string
appVersion string
}
Expand Down Expand Up @@ -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) {
Expand Down
11 changes: 8 additions & 3 deletions baseapp/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 11 additions & 4 deletions server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ 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.
HaltHeight uint64 `mapstructure:"halt-height"`

// HaltTime contains a non-zero minimum block time at which a node will
// gracefully halt and shutdown that can be used to assist upgrades and testing.
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 +66,8 @@ func (c *Config) GetMinGasPrices() sdk.DecCoins {
func DefaultConfig() *Config {
return &Config{
BaseConfig{
MinGasPrices: defaultMinGasPrices,
HaltHeight: 0,
MinGasPrices: defaultMinGasPrices,
InterBlockCache: true,
},
}
}
11 changes: 9 additions & 2 deletions server/config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ 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.
halt-height = {{ .BaseConfig.HaltHeight }}

# HaltTime contains a non-zero minimum block time at which a node will
# gracefully halt and shutdown that can be used to assist upgrades and testing.
halt-time = {{ .BaseConfig.HaltTime }}

# InterBlockCache enables inter-block caching.
inter-block-cache = {{ .BaseConfig.InterBlockCache }}
`

var configTemplate *template.Template
Expand Down
4 changes: 3 additions & 1 deletion 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"
FlagInterBlockCache = "inter-block-cache"
)

Expand Down Expand Up @@ -58,7 +59,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 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")

Expand Down