From 9d840638403fff9b37ac5557a9d66961fd888938 Mon Sep 17 00:00:00 2001 From: Tsachi Herman Date: Thu, 2 Jan 2020 16:41:32 -0500 Subject: [PATCH 1/2] Make gracefull exit of a node that is waiting for WaitForBlock call. --- daemon/algod/api/server/lib/common.go | 5 ++- daemon/algod/api/server/router.go | 4 +- daemon/algod/api/server/v1/handlers/errors.go | 1 + .../algod/api/server/v1/handlers/handlers.go | 3 ++ daemon/algod/server.go | 40 ++++++++----------- 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/daemon/algod/api/server/lib/common.go b/daemon/algod/api/server/lib/common.go index 736d6f557b..1d2576cdca 100644 --- a/daemon/algod/api/server/lib/common.go +++ b/daemon/algod/api/server/lib/common.go @@ -43,8 +43,9 @@ type Routes []Route // ReqContext is passed to each of the handlers below via wrapCtx, allowing // handlers to interact with the node type ReqContext struct { - Node *node.AlgorandFullNode - Log logging.Logger + Node *node.AlgorandFullNode + Log logging.Logger + Shutdown <-chan struct{} } // ErrorResponse sets the specified status code (should != 200), and fills in the diff --git a/daemon/algod/api/server/router.go b/daemon/algod/api/server/router.go index 8697dd73d7..b1fa3288eb 100644 --- a/daemon/algod/api/server/router.go +++ b/daemon/algod/api/server/router.go @@ -106,7 +106,7 @@ func registerHandlers(router *mux.Router, prefix string, routes lib.Routes, ctx } // NewRouter builds and returns a new router from routes -func NewRouter(logger logging.Logger, node *node.AlgorandFullNode, apiToken string) *mux.Router { +func NewRouter(logger logging.Logger, node *node.AlgorandFullNode, shutdown <-chan struct{}, apiToken string) *mux.Router { router := mux.NewRouter().StrictSlash(true) // Middleware @@ -115,7 +115,7 @@ func NewRouter(logger logging.Logger, node *node.AlgorandFullNode, apiToken stri router.Use(middlewares.CORS) // Request Context - ctx := lib.ReqContext{Node: node, Log: logger} + ctx := lib.ReqContext{Node: node, Log: logger, Shutdown: shutdown} // Registers /debug/pprof handler under root path and under /urlAuth path // to support header or url-provided token. diff --git a/daemon/algod/api/server/v1/handlers/errors.go b/daemon/algod/api/server/v1/handlers/errors.go index 58992e25ce..c5e6540cd7 100644 --- a/daemon/algod/api/server/v1/handlers/errors.go +++ b/daemon/algod/api/server/v1/handlers/errors.go @@ -40,5 +40,6 @@ var ( errNoRoundsSpecified = "Indexer is not enabled, firstRound and lastRound must be specified" errNoTxnSpecified = "no transaction ID was specified" errTransactionNotFound = "couldn't find the required transaction in the required range" + errServiceShuttingDown = "operation aborted as server is shutting down" errUnknownTransactionType = "found a transaction with an unknown type" ) diff --git a/daemon/algod/api/server/v1/handlers/handlers.go b/daemon/algod/api/server/v1/handlers/handlers.go index 98ae8529a5..665923425d 100644 --- a/daemon/algod/api/server/v1/handlers/handlers.go +++ b/daemon/algod/api/server/v1/handlers/handlers.go @@ -419,6 +419,9 @@ func WaitForBlock(ctx lib.ReqContext, w http.ResponseWriter, r *http.Request) { } select { + case <-ctx.Shutdown: + lib.ErrorResponse(w, http.StatusInternalServerError, err, errServiceShuttingDown, ctx.Log) + return case <-time.After(1 * time.Minute): case <-ctx.Node.Ledger().Wait(basics.Round(queryRound + 1)): } diff --git a/daemon/algod/server.go b/daemon/algod/server.go index c2f2732d3e..f83603ea5a 100644 --- a/daemon/algod/server.go +++ b/daemon/algod/server.go @@ -55,9 +55,7 @@ type Server struct { node *node.AlgorandFullNode metricCollector *metrics.MetricService metricServiceStarted bool - - stopping deadlock.Mutex - stopped bool + stopping chan struct{} } // Initialize creates a Node instance with applicable network services @@ -178,9 +176,11 @@ func (s *Server) Start() { os.Exit(1) } + s.stopping = make(chan struct{}) + // use the data dir as the static file dir (for our API server), there's // no need to separate the two yet. This lets us serve the swagger.json file. - apiHandler := apiServer.NewRouter(s.log, s.node, apiToken) + apiHandler := apiServer.NewRouter(s.log, s.node, s.stopping, apiToken) addr := cfg.EndpointAddress if addr == "" { @@ -202,8 +202,6 @@ func (s *Server) Start() { WriteTimeout: time.Duration(cfg.RestWriteTimeoutSeconds) * time.Second, } - defer s.Stop() - tcpListener := listener.(*net.TCPListener) errChan := make(chan error, 1) go func() { @@ -227,30 +225,26 @@ func (s *Server) Start() { c := make(chan os.Signal) signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) signal.Ignore(syscall.SIGHUP) - go func() { - sig := <-c + + fmt.Printf("Node running and accepting RPC requests over HTTP on port %v. Press Ctrl-C to exit\n", addr) + select { + case err := <-errChan: + if err != nil { + s.log.Warn(err) + } else { + s.log.Info("Node exited successfully") + } + s.Stop() + case sig := <-c: fmt.Printf("Exiting on %v\n", sig) s.Stop() os.Exit(0) - }() - - fmt.Printf("Node running and accepting RPC requests over HTTP on port %v. Press Ctrl-C to exit\n", addr) - err = <-errChan - if err != nil { - s.log.Warn(err) - } else { - s.log.Info("Node exited successfully") } } // Stop initiates a graceful shutdown of the node by shutting down the network server. func (s *Server) Stop() { - s.stopping.Lock() - defer s.stopping.Unlock() - - if s.stopped { - return - } + close(s.stopping) // Attempt to log a shutdown event before we exit... s.log.Event(telemetryspec.ApplicationState, telemetryspec.ShutdownEvent) @@ -275,8 +269,6 @@ func (s *Server) Stop() { os.Remove(s.pidFile) os.Remove(s.netFile) os.Remove(s.netListenFile) - - s.stopped = true } // OverridePhonebook is used to replace the phonebook associated with From b17f3c7146752833b7aa5cf38466d30a86e233b0 Mon Sep 17 00:00:00 2001 From: Tsachi Herman Date: Thu, 2 Jan 2020 18:45:50 -0500 Subject: [PATCH 2/2] Add comment. --- daemon/algod/server.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/daemon/algod/server.go b/daemon/algod/server.go index f83603ea5a..015d4b76b3 100644 --- a/daemon/algod/server.go +++ b/daemon/algod/server.go @@ -244,6 +244,8 @@ func (s *Server) Start() { // Stop initiates a graceful shutdown of the node by shutting down the network server. func (s *Server) Stop() { + // close the s.stopping, which would signal the rest api router that any pending commands + // should be aborted. close(s.stopping) // Attempt to log a shutdown event before we exit...