imp(server): Add query-only mode flag for gRPC (#1360)

* add grpc only mode flag

* add change log

* add tmNode nil check

* only flag to modes

* fix close for query mode

* fix hint

* keep grpc-only & json-rpc-only

* rm space

* mv apiSrv close

* mv cpuProfileCleanup

* mv tmNode stop

* disable indexer on query only

which need tm client on start

* update nix

* revert json-rpc-only change
This commit is contained in:
mmsqe 2022-10-19 17:47:34 +08:00 committed by GitHub
parent db94d8ec23
commit 236ca33c53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 88 additions and 87 deletions

View File

@ -60,6 +60,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (ledger) [#1277](https://github.com/evmos/ethermint/pull/1277) Add Ledger preprocessing transaction hook for EIP-712-signed Cosmos payloads.
* (rpc) [#1296](https://github.com/evmos/ethermint/pull/1296) Add RPC Backend unit tests.
* (rpc) [#1352](https://github.com/evmos/ethermint/pull/1352) Make the grpc queries run concurrently, don't block the consensus state machine.
* (cli) [#1360](https://github.com/evmos/ethermint/pull/1360) Introduce a new `grpc-only` flag, such that when enabled, will start the node in a query-only mode. Note, gRPC MUST be enabled with this flag.
* (rpc) [#1378](https://github.com/evmos/ethermint/pull/1378) Add support for EVM RPC metrics
### Bug Fixes

View File

@ -20,6 +20,7 @@ const (
// GRPC-related flags.
const (
GRPCOnly = "grpc-only"
GRPCEnable = "grpc.enable"
GRPCAddress = "grpc.address"
GRPCWebEnable = "grpc-web.enable"

View File

@ -150,6 +150,7 @@ which accepts a path for the resulting pprof file.
cmd.Flags().Uint64(server.FlagMinRetainBlocks, 0, "Minimum block height offset during ABCI commit to prune Tendermint blocks")
cmd.Flags().String(srvflags.AppDBBackend, "", "The type of database for application and snapshots databases")
cmd.Flags().Bool(srvflags.GRPCOnly, false, "Start the node in gRPC query only mode without Tendermint process")
cmd.Flags().Bool(srvflags.GRPCEnable, true, "Define if the gRPC server should be enabled")
cmd.Flags().String(srvflags.GRPCAddress, serverconfig.DefaultGRPCAddress, "the gRPC server address to listen on")
cmd.Flags().Bool(srvflags.GRPCWebEnable, true, "Define if the gRPC-Web server should be enabled. (Note: gRPC must also be enabled.)")
@ -158,7 +159,7 @@ which accepts a path for the resulting pprof file.
cmd.Flags().Bool(srvflags.RPCEnable, false, "Defines if Cosmos-sdk REST server should be enabled")
cmd.Flags().Bool(srvflags.EnabledUnsafeCors, false, "Defines if CORS should be enabled (unsafe - use it at your own risk)")
cmd.Flags().Bool(srvflags.JSONRPCEnable, true, "Define if the gRPC server should be enabled")
cmd.Flags().Bool(srvflags.JSONRPCEnable, true, "Define if the JSON-RPC server should be enabled")
cmd.Flags().StringSlice(srvflags.JSONRPCAPI, config.GetDefaultAPINamespaces(), "Defines a list of JSON-RPC namespaces that should be enabled")
cmd.Flags().String(srvflags.JSONRPCAddress, config.DefaultJSONRPCAddress, "the JSON-RPC server address to listen on")
cmd.Flags().String(srvflags.JSONWsAddress, config.DefaultJSONRPCWsAddress, "the JSON-RPC WS server address to listen on")
@ -239,8 +240,6 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
cfg := ctx.Config
home := cfg.RootDir
logger := ctx.Logger
var cpuProfileCleanup func() error
if cpuProfile := ctx.Viper.GetString(srvflags.CPUProfile); cpuProfile != "" {
fp, err := ethdebug.ExpandHome(cpuProfile)
if err != nil {
@ -257,15 +256,13 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
return err
}
cpuProfileCleanup = func() error {
defer func() {
ctx.Logger.Info("stopping CPU profiler", "profile", cpuProfile)
pprof.StopCPUProfile()
if err := f.Close(); err != nil {
logger.Error("failed to close CPU profiler file", "error", err.Error())
return err
}
return nil
}
}()
}
traceWriterFile := ctx.Viper.GetString(srvflags.TraceStore)
@ -313,30 +310,47 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
}
genDocProvider := node.DefaultGenesisDocProviderFunc(cfg)
tmNode, err := node.NewNode(
cfg,
pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()),
nodeKey,
proxy.NewLocalClientCreator(app),
genDocProvider,
node.DefaultDBProvider,
node.DefaultMetricsProvider(cfg.Instrumentation),
ctx.Logger.With("server", "node"),
var (
tmNode *node.Node
gRPCOnly = ctx.Viper.GetBool(srvflags.GRPCOnly)
)
if err != nil {
logger.Error("failed init node", "error", err.Error())
return err
}
if gRPCOnly {
ctx.Logger.Info("starting node in query only mode; Tendermint is disabled")
config.GRPC.Enable = true
config.JSONRPC.EnableIndexer = false
} else {
tmNode, err = node.NewNode(
cfg,
pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()),
nodeKey,
proxy.NewLocalClientCreator(app),
genDocProvider,
node.DefaultDBProvider,
node.DefaultMetricsProvider(cfg.Instrumentation),
ctx.Logger.With("server", "node"),
)
if err != nil {
logger.Error("failed init node", "error", err.Error())
return err
}
if err := tmNode.Start(); err != nil {
logger.Error("failed start tendermint server", "error", err.Error())
return err
if err := tmNode.Start(); err != nil {
logger.Error("failed start tendermint server", "error", err.Error())
return err
}
defer func() {
if tmNode.IsRunning() {
_ = tmNode.Stop()
}
logger.Info("Bye!")
}()
}
// Add the tx service to the gRPC router. We only need to register this
// service if API or gRPC or JSONRPC is enabled, and avoid doing so in the general
// case, because it spawns a new local tendermint RPC client.
if config.API.Enable || config.GRPC.Enable || config.JSONRPC.Enable || config.JSONRPC.EnableIndexer {
if (config.API.Enable || config.GRPC.Enable || config.JSONRPC.Enable || config.JSONRPC.EnableIndexer) && tmNode != nil {
clientCtx = clientCtx.WithClient(local.New(tmNode))
app.RegisterTxService(clientCtx)
@ -429,7 +443,6 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
apiSrv = api.New(clientCtx, ctx.Logger.With("server", "api"))
app.RegisterAPIRoutes(apiSrv, config.API)
errCh := make(chan error)
go func() {
if err := apiSrv.Start(config.Config); err != nil {
errCh <- err
@ -441,6 +454,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
return err
case <-time.After(types.ServerStartTime): // assume server started successfully
}
defer apiSrv.Close()
}
var (
@ -452,15 +466,62 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
if err != nil {
return err
}
defer grpcSrv.Stop()
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)
return err
}
defer func() {
if err := grpcWebSrv.Close(); err != nil {
logger.Error("failed to close the grpcWebSrc", "error", err.Error())
}
}()
}
}
var (
httpSrv *http.Server
httpSrvDone chan struct{}
)
if config.JSONRPC.Enable {
genDoc, err := genDocProvider()
if err != nil {
return err
}
clientCtx := clientCtx.WithChainID(genDoc.ChainID)
tmEndpoint := "/websocket"
tmRPCAddr := cfg.RPC.ListenAddress
httpSrv, httpSrvDone, err = StartJSONRPC(ctx, clientCtx, tmRPCAddr, tmEndpoint, &config, idxer)
if err != nil {
return err
}
defer func() {
shutdownCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
defer cancelFn()
if err := httpSrv.Shutdown(shutdownCtx); err != nil {
logger.Error("HTTP server shutdown produced a warning", "error", err.Error())
} else {
logger.Info("HTTP server shut down, waiting 5 sec")
select {
case <-time.Tick(5 * time.Second):
case <-httpSrvDone:
}
}
}()
}
// At this point it is safe to block the process if we're in query only mode as
// we do not need to start Rosetta or handle any Tendermint related processes.
if gRPCOnly {
// wait for signal capture and gracefully return
return server.WaitForQuitSignals()
}
var rosettaSrv crgserver.Server
if config.Rosetta.Enable {
offlineMode := config.Rosetta.Offline
@ -496,68 +557,6 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
case <-time.After(types.ServerStartTime): // assume server started successfully
}
}
var (
httpSrv *http.Server
httpSrvDone chan struct{}
)
if config.JSONRPC.Enable {
genDoc, err := genDocProvider()
if err != nil {
return err
}
clientCtx := clientCtx.WithChainID(genDoc.ChainID)
tmEndpoint := "/websocket"
tmRPCAddr := cfg.RPC.ListenAddress
httpSrv, httpSrvDone, err = StartJSONRPC(ctx, clientCtx, tmRPCAddr, tmEndpoint, &config, idxer)
if err != nil {
return err
}
}
defer func() {
if tmNode.IsRunning() {
_ = tmNode.Stop()
}
if cpuProfileCleanup != nil {
_ = cpuProfileCleanup()
}
if apiSrv != nil {
_ = apiSrv.Close()
}
if grpcSrv != nil {
grpcSrv.Stop()
if grpcWebSrv != nil {
if err := grpcWebSrv.Close(); err != nil {
logger.Error("failed to close the grpcWebSrc", "error", err.Error())
}
}
}
if httpSrv != nil {
shutdownCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
defer cancelFn()
if err := httpSrv.Shutdown(shutdownCtx); err != nil {
logger.Error("HTTP server shutdown produced a warning", "error", err.Error())
} else {
logger.Info("HTTP server shut down, waiting 5 sec")
select {
case <-time.Tick(5 * time.Second):
case <-httpSrvDone:
}
}
}
logger.Info("Bye!")
}()
// Wait for SIGINT or SIGTERM signal
return server.WaitForQuitSignals()
}