I have a gRPC server that hosts two asynchronous services ("Master" and "Worker"), and I would like to implement graceful shutdown for the server. Each service has its own grpc::CompletionQueue
.
There appear to be two Shutdown()
methods that might be relevant: grpc::CompletionQueue::Shutdown()
and grpc::Server::Shutdown()
, but it's not clear from the documentation which ones should be used.
What is a good pattern for shutting down an asynchronous service?
overridevirtual Block until the server shuts down.
Warning The server must be either shutting down or some other thread must call Shutdown for this function to ever return.
http://static.grumpycoder.net/pixel/ref/c++/html/classgrpc_1_1_server.html
TL;DR: You must call both
grpc::Server::Shutdown()
andgrpc::CompletionQueue::Shutdown()
(for each completion queue used in the service) to shut down cleanly.If you call
cq_->Shutdown()
, the only observable effect is that subsequent calls toService::AsyncService::RequestFoo()
(the generated method for the correspondingFoo
RPC) fail with an assertion. From reading the documentation of the corresponding C API method (grpc_completion_queue_shutdown()
), it appears that it is illegal to add new work to the queue—i.e. by callingRequestFoo()
—so I added anis_shutdown_
member to my service wrapper classes (protected by a mutex) so that no enqueue attempts are made aftercq_->Shutdown()
is called. However, after doing this, the completion queue blocks indefinitely incq_->Next()
. None of the enqueued tags complete (with an error or otherwise).If instead you call
server_->Shutdown()
, all of the enqueued tags complete immediately (withok == false
). However, the completion queue continues to block indefinitely incq_->Next()
.Calling both
cq_->Shutdown()
(for each defined completion queue) andserver_->Shutdown()
results in a clean shutdown.One caveat: if you use
grpc::ServerContext::AsyncNotifyWhenDone()
to register a tag for call cancellation, these will not be returned bycq_->Next()
if the server shuts down before the initial request is received for that call. You will need to be cautious with the memory management of the corresponding tag structure, if you want to avoid memory leaks.