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

Problem: abci handshake is not shutdown gracefully #288

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (ante) [#227](https://github.com/crypto-org-chain/ethermint/pull/227) Reuse sender recovery result.
* (cli) [#242](https://github.com/crypto-org-chain/ethermint/pull/242) Integrate tendermint bootstrap cmd.
* (cli) [#246](https://github.com/crypto-org-chain/ethermint/pull/246) Call app.Close to cleanup resource on graceful shutdown.
* (cli) [#]() make abci handshake shutdown gracefully.
yihuang marked this conversation as resolved.
Show resolved Hide resolved

### State Machine Breaking

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ replace (
github.com/miguelmota/go-ethereum-hdwallet => github.com/crypto-org-chain/go-ethereum-hdwallet v0.1.2
github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
// use cometbft
github.com/tendermint/tendermint => github.com/cometbft/cometbft v0.34.28
github.com/tendermint/tendermint => github.com/yihuang/cometbft v0.34.28-0.20230621082015-c42075bf65d7
yihuang marked this conversation as resolved.
Show resolved Hide resolved
// https://github.com/crypto-org-chain/tm-db/tree/release/v0.6.x
github.com/tendermint/tm-db => github.com/crypto-org-chain/tm-db v0.6.8-0.20230424032152-87c7e7f4fb61
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,6 @@ github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcju
github.com/coinbase/kryptology v1.8.0/go.mod h1:RYXOAPdzOGUe3qlSFkMGn58i3xUA8hmxYHksuq+8ciI=
github.com/coinbase/rosetta-sdk-go v0.7.9 h1:lqllBjMnazTjIqYrOGv8h8jxjg9+hJazIGZr9ZvoCcA=
github.com/coinbase/rosetta-sdk-go v0.7.9/go.mod h1:0/knutI7XGVqXmmH4OQD8OckFrbQ8yMsUZTG7FXCR2M=
github.com/cometbft/cometbft v0.34.28 h1:gwryf55P1SWMUP4nOXpRVI2D0yPoYEzN+IBqmRBOsDc=
github.com/cometbft/cometbft v0.34.28/go.mod h1:L9shMfbkZ8B+7JlwANEr+NZbBcn+hBpwdbeYvA5rLCw=
github.com/cometbft/cometbft-db v0.8.0 h1:vUMDaH3ApkX8m0KZvOFFy9b5DZHBAjsnEuo9AKVZpjo=
github.com/cometbft/cometbft-db v0.8.0/go.mod h1:6ASCP4pfhmrCBpfk01/9E1SI29nD3HfVHrY4PG8x5c0=
github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4=
Expand Down Expand Up @@ -1090,6 +1088,8 @@ github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6Ut
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
github.com/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE=
github.com/yihuang/cometbft v0.34.28-0.20230621082015-c42075bf65d7 h1:JG9qrxJaH8DNRpF9W4T21QSWAwvneIU0PAH60aMufN0=
github.com/yihuang/cometbft v0.34.28-0.20230621082015-c42075bf65d7/go.mod h1:L9shMfbkZ8B+7JlwANEr+NZbBcn+hBpwdbeYvA5rLCw=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
Expand Down
68 changes: 32 additions & 36 deletions server/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/telemetry"
"golang.org/x/sync/errgroup"

"github.com/spf13/cobra"

Expand Down Expand Up @@ -272,20 +273,20 @@ func startStandAlone(ctx *server.Context, opts StartOptions) error {
}

// legacyAminoCdc is used for the legacy REST API
func startInProcess(ctx *server.Context, clientCtx client.Context, opts StartOptions) (err error) {
cfg := ctx.Config
func startInProcess(svrCtx *server.Context, clientCtx client.Context, opts StartOptions) (err error) {
cfg := svrCtx.Config
home := cfg.RootDir
logger := ctx.Logger
logger := svrCtx.Logger

traceWriterFile := ctx.Viper.GetString(srvflags.TraceStore)
db, err := opts.DBOpener(home, server.GetAppDBBackend(ctx.Viper))
traceWriterFile := svrCtx.Viper.GetString(srvflags.TraceStore)
db, err := opts.DBOpener(home, server.GetAppDBBackend(svrCtx.Viper))
if err != nil {
logger.Error("failed to open DB", "error", err.Error())
return err
}
defer func() {
if err := db.Close(); err != nil {
ctx.Logger.With("error", err).Error("error closing db")
svrCtx.Logger.With("error", err).Error("error closing db")
}
}()

Expand All @@ -295,15 +296,15 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, opts StartOpt
return err
}

config, err := config.GetConfig(ctx.Viper)
config, err := config.GetConfig(svrCtx.Viper)
if err != nil {
logger.Error("failed to get server config", "error", err.Error())
return err
}

if err := config.ValidateBasic(); err != nil {
if strings.Contains(err.Error(), "set min gas price in app.toml or flag or env variable") {
ctx.Logger.Error(
svrCtx.Logger.Error(
"WARNING: The minimum-gas-prices config in app.toml is set to the empty string. " +
"This defaults to 0 in the current version, but will error in the next version " +
"(SDK v0.44). Please explicitly put the desired minimum-gas-prices in your app.toml.",
Expand All @@ -313,33 +314,40 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, opts StartOpt
}
}

app := opts.AppCreator(ctx.Logger, db, traceWriter, ctx.Viper)
app := opts.AppCreator(svrCtx.Logger, db, traceWriter, svrCtx.Viper)

nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile())
if err != nil {
logger.Error("failed load or gen node key", "error", err.Error())
return err
}

ctx, cancelFn := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)

// listen for quit signals so the calling parent process can gracefully exit
ListenForQuitSignals(g, true, cancelFn, svrCtx.Logger)

genDocProvider := node.DefaultGenesisDocProviderFunc(cfg)
var (
tmNode *node.Node
gRPCOnly = ctx.Viper.GetBool(srvflags.GRPCOnly)
gRPCOnly = svrCtx.Viper.GetBool(srvflags.GRPCOnly)
)
if gRPCOnly {
ctx.Logger.Info("starting node in query only mode; Tendermint is disabled")
svrCtx.Logger.Info("starting node in query only mode; Tendermint is disabled")
config.GRPC.Enable = true
config.JSONRPC.EnableIndexer = false
} else {
tmNode, err = node.NewNode(
tmNode, err = node.NewNodeWithContext(
ctx,
cfg,
pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()),
nodeKey,
proxy.NewLocalClientCreator(app),
genDocProvider,
node.DefaultDBProvider,
node.DefaultMetricsProvider(cfg.Instrumentation),
ctx.Logger.With("server", "node"),
svrCtx.Logger.With("server", "node"),
)
if err != nil {
logger.Error("failed init node", "error", err.Error())
Expand Down Expand Up @@ -372,18 +380,18 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, opts StartOpt

// Enable metrics if JSONRPC is enabled and --metrics is passed
// Flag not added in config to avoid user enabling in config without passing in CLI
if config.JSONRPC.Enable && ctx.Viper.GetBool(srvflags.JSONRPCEnableMetrics) {
if config.JSONRPC.Enable && svrCtx.Viper.GetBool(srvflags.JSONRPCEnableMetrics) {
ethmetricsexp.Setup(config.JSONRPC.MetricsAddress)
}

var idxer ethermint.EVMTxIndexer
if config.JSONRPC.EnableIndexer {
idxDB, err := OpenIndexerDB(home, server.GetAppDBBackend(ctx.Viper))
idxDB, err := OpenIndexerDB(home, server.GetAppDBBackend(svrCtx.Viper))
if err != nil {
logger.Error("failed to open evm indexer DB", "error", err.Error())
return err
}
idxLogger := ctx.Logger.With("module", "evmindex")
idxLogger := svrCtx.Logger.With("module", "evmindex")
idxer = indexer.NewKVIndexer(idxDB, idxLogger, clientCtx)
indexerService := NewEVMIndexerService(idxer, clientCtx.Client)
indexerService.SetLogger(idxLogger)
Expand Down Expand Up @@ -447,7 +455,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, opts StartOpt
}

clientCtx = clientCtx.WithGRPCClient(grpcClient)
ctx.Logger.Debug("gRPC client assigned to client context", "address", grpcAddress)
svrCtx.Logger.Debug("gRPC client assigned to client context", "address", grpcAddress)
}
}

Expand All @@ -458,7 +466,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, opts StartOpt

var apiSrv *api.Server
if config.API.Enable {
apiSrv = api.New(clientCtx, ctx.Logger.With("server", "api"))
apiSrv = api.New(clientCtx, svrCtx.Logger.With("server", "api"))
app.RegisterAPIRoutes(apiSrv, config.API)
if config.Telemetry.Enabled {
apiSrv.SetTelemetry(metrics)
Expand Down Expand Up @@ -491,7 +499,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, opts StartOpt
if config.GRPCWeb.Enable {
grpcWebSrv, err = servergrpc.StartGRPCWeb(grpcSrv, config.Config)
if err != nil {
ctx.Logger.Error("failed to start grpc-web http server", "error", err)
svrCtx.Logger.Error("failed to start grpc-web http server", "error", err)
return err
}
defer func() {
Expand All @@ -517,7 +525,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, opts StartOpt

tmEndpoint := "/websocket"
tmRPCAddr := cfg.RPC.ListenAddress
httpSrv, httpSrvDone, err = StartJSONRPC(ctx, clientCtx, tmRPCAddr, tmEndpoint, &config, idxer)
httpSrv, httpSrvDone, err = StartJSONRPC(svrCtx, clientCtx, tmRPCAddr, tmEndpoint, &config, idxer)
if err != nil {
return err
}
Expand Down Expand Up @@ -553,7 +561,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, opts StartOpt
conf := &rosetta.Config{
Blockchain: config.Rosetta.Blockchain,
Network: config.Rosetta.Network,
TendermintRPC: ctx.Config.RPC.ListenAddress,
TendermintRPC: svrCtx.Config.RPC.ListenAddress,
GRPCEndpoint: config.GRPC.Address,
Addr: config.Rosetta.Address,
Retries: config.Rosetta.Retries,
Expand All @@ -578,8 +586,8 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, opts StartOpt
case <-time.After(types.ServerStartTime): // assume server started successfully
}
}
// Wait for SIGINT or SIGTERM signal
return server.WaitForQuitSignals()

return g.Wait()
}

func openDB(rootDir string, backendType dbm.BackendType) (dbm.DB, error) {
Expand Down Expand Up @@ -640,17 +648,5 @@ func wrapCPUProfile(ctx *server.Context, callback func() error) error {
}()
}

errCh := make(chan error)
go func() {
errCh <- callback()
}()

select {
case err := <-errCh:
return err

case <-time.After(types.ServerStartTime):
}

return server.WaitForQuitSignals()
return callback()
}
32 changes: 32 additions & 0 deletions server/util.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package server

import (
"context"
"net"
"net/http"
"os"
"os/signal"
"syscall"
"time"

"github.com/evmos/ethermint/server/config"
"github.com/gorilla/mux"
"github.com/improbable-eng/grpc-web/go/grpcweb"
"github.com/spf13/cobra"
"golang.org/x/net/netutil"
"golang.org/x/sync/errgroup"

sdkserver "github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/server/types"
Expand Down Expand Up @@ -124,3 +129,30 @@ func Listen(addr string, config *config.Config) (net.Listener, error) {
}
return ln, err
}

// ListenForQuitSignals listens for SIGINT and SIGTERM. When a signal is received,
// the cleanup function is called, indicating the caller can gracefully exit or
// return.
//
// Note, the blocking behavior of this depends on the block argument.
// The caller must ensure the corresponding context derived from the cancelFn is used correctly.
func ListenForQuitSignals(g *errgroup.Group, block bool, cancelFn context.CancelFunc, logger tmlog.Logger) {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

f := func() {
sig := <-sigCh
cancelFn()

logger.Info("caught signal", "signal", sig.String())
}

if block {
g.Go(func() error {
f()
return nil
})
} else {
go f()
}
}