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. * (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) [#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. * (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 * (rpc) [#1378](https://github.com/evmos/ethermint/pull/1378) Add support for EVM RPC metrics
### Bug Fixes ### Bug Fixes

View File

@ -20,6 +20,7 @@ const (
// GRPC-related flags. // GRPC-related flags.
const ( const (
GRPCOnly = "grpc-only"
GRPCEnable = "grpc.enable" GRPCEnable = "grpc.enable"
GRPCAddress = "grpc.address" GRPCAddress = "grpc.address"
GRPCWebEnable = "grpc-web.enable" 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().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().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().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().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.)") 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.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.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().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.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") 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 cfg := ctx.Config
home := cfg.RootDir home := cfg.RootDir
logger := ctx.Logger logger := ctx.Logger
var cpuProfileCleanup func() error
if cpuProfile := ctx.Viper.GetString(srvflags.CPUProfile); cpuProfile != "" { if cpuProfile := ctx.Viper.GetString(srvflags.CPUProfile); cpuProfile != "" {
fp, err := ethdebug.ExpandHome(cpuProfile) fp, err := ethdebug.ExpandHome(cpuProfile)
if err != nil { if err != nil {
@ -257,15 +256,13 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
return err return err
} }
cpuProfileCleanup = func() error { defer func() {
ctx.Logger.Info("stopping CPU profiler", "profile", cpuProfile) ctx.Logger.Info("stopping CPU profiler", "profile", cpuProfile)
pprof.StopCPUProfile() pprof.StopCPUProfile()
if err := f.Close(); err != nil { if err := f.Close(); err != nil {
logger.Error("failed to close CPU profiler file", "error", err.Error()) logger.Error("failed to close CPU profiler file", "error", err.Error())
return err
} }
return nil }()
}
} }
traceWriterFile := ctx.Viper.GetString(srvflags.TraceStore) traceWriterFile := ctx.Viper.GetString(srvflags.TraceStore)
@ -313,30 +310,47 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
} }
genDocProvider := node.DefaultGenesisDocProviderFunc(cfg) genDocProvider := node.DefaultGenesisDocProviderFunc(cfg)
tmNode, err := node.NewNode( var (
cfg, tmNode *node.Node
pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()), gRPCOnly = ctx.Viper.GetBool(srvflags.GRPCOnly)
nodeKey,
proxy.NewLocalClientCreator(app),
genDocProvider,
node.DefaultDBProvider,
node.DefaultMetricsProvider(cfg.Instrumentation),
ctx.Logger.With("server", "node"),
) )
if err != nil { if gRPCOnly {
logger.Error("failed init node", "error", err.Error()) ctx.Logger.Info("starting node in query only mode; Tendermint is disabled")
return err 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 { if err := tmNode.Start(); err != nil {
logger.Error("failed start tendermint server", "error", err.Error()) logger.Error("failed start tendermint server", "error", err.Error())
return err 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 // 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 // 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. // 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)) clientCtx = clientCtx.WithClient(local.New(tmNode))
app.RegisterTxService(clientCtx) 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")) apiSrv = api.New(clientCtx, ctx.Logger.With("server", "api"))
app.RegisterAPIRoutes(apiSrv, config.API) app.RegisterAPIRoutes(apiSrv, config.API)
errCh := make(chan error) errCh := make(chan error)
go func() { go func() {
if err := apiSrv.Start(config.Config); err != nil { if err := apiSrv.Start(config.Config); err != nil {
errCh <- err errCh <- err
@ -441,6 +454,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
return err return err
case <-time.After(types.ServerStartTime): // assume server started successfully case <-time.After(types.ServerStartTime): // assume server started successfully
} }
defer apiSrv.Close()
} }
var ( var (
@ -452,15 +466,62 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
if err != nil { if err != nil {
return err return err
} }
defer grpcSrv.Stop()
if config.GRPCWeb.Enable { if config.GRPCWeb.Enable {
grpcWebSrv, err = servergrpc.StartGRPCWeb(grpcSrv, config.Config) grpcWebSrv, err = servergrpc.StartGRPCWeb(grpcSrv, config.Config)
if err != nil { if err != nil {
ctx.Logger.Error("failed to start grpc-web http server", "error", err) ctx.Logger.Error("failed to start grpc-web http server", "error", err)
return 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 var rosettaSrv crgserver.Server
if config.Rosetta.Enable { if config.Rosetta.Enable {
offlineMode := config.Rosetta.Offline 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 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 // Wait for SIGINT or SIGTERM signal
return server.WaitForQuitSignals() return server.WaitForQuitSignals()
} }