|
|
|
@ -18,6 +18,7 @@ |
|
|
|
|
|
|
|
|
|
#include "src/cpp/server/health/default_health_check_service.h" |
|
|
|
|
|
|
|
|
|
#include <stddef.h> |
|
|
|
|
#include <stdint.h> |
|
|
|
|
|
|
|
|
|
#include <memory> |
|
|
|
@ -29,7 +30,6 @@ |
|
|
|
|
|
|
|
|
|
#include <grpc/slice.h> |
|
|
|
|
#include <grpc/support/log.h> |
|
|
|
|
#include <grpcpp/impl/codegen/server_callback_handlers.h> |
|
|
|
|
#include <grpcpp/impl/rpc_method.h> |
|
|
|
|
#include <grpcpp/impl/rpc_service_method.h> |
|
|
|
|
#include <grpcpp/support/slice.h> |
|
|
|
@ -50,7 +50,7 @@ DefaultHealthCheckService::DefaultHealthCheckService() { |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::SetServingStatus( |
|
|
|
|
const std::string& service_name, bool serving) { |
|
|
|
|
grpc::internal::MutexLock lock(&mu_); |
|
|
|
|
grpc_core::MutexLock lock(&mu_); |
|
|
|
|
if (shutdown_) { |
|
|
|
|
// Set to NOT_SERVING in case service_name is not in the map.
|
|
|
|
|
serving = false; |
|
|
|
@ -60,8 +60,10 @@ void DefaultHealthCheckService::SetServingStatus( |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::SetServingStatus(bool serving) { |
|
|
|
|
const ServingStatus status = serving ? SERVING : NOT_SERVING; |
|
|
|
|
grpc::internal::MutexLock lock(&mu_); |
|
|
|
|
if (shutdown_) return; |
|
|
|
|
grpc_core::MutexLock lock(&mu_); |
|
|
|
|
if (shutdown_) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
for (auto& p : services_map_) { |
|
|
|
|
ServiceData& service_data = p.second; |
|
|
|
|
service_data.SetServingStatus(status); |
|
|
|
@ -69,8 +71,10 @@ void DefaultHealthCheckService::SetServingStatus(bool serving) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::Shutdown() { |
|
|
|
|
grpc::internal::MutexLock lock(&mu_); |
|
|
|
|
if (shutdown_) return; |
|
|
|
|
grpc_core::MutexLock lock(&mu_); |
|
|
|
|
if (shutdown_) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
shutdown_ = true; |
|
|
|
|
for (auto& p : services_map_) { |
|
|
|
|
ServiceData& service_data = p.second; |
|
|
|
@ -81,37 +85,43 @@ void DefaultHealthCheckService::Shutdown() { |
|
|
|
|
DefaultHealthCheckService::ServingStatus |
|
|
|
|
DefaultHealthCheckService::GetServingStatus( |
|
|
|
|
const std::string& service_name) const { |
|
|
|
|
grpc::internal::MutexLock lock(&mu_); |
|
|
|
|
grpc_core::MutexLock lock(&mu_); |
|
|
|
|
auto it = services_map_.find(service_name); |
|
|
|
|
if (it == services_map_.end()) return NOT_FOUND; |
|
|
|
|
if (it == services_map_.end()) { |
|
|
|
|
return NOT_FOUND; |
|
|
|
|
} |
|
|
|
|
const ServiceData& service_data = it->second; |
|
|
|
|
return service_data.GetServingStatus(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::RegisterWatch( |
|
|
|
|
void DefaultHealthCheckService::RegisterCallHandler( |
|
|
|
|
const std::string& service_name, |
|
|
|
|
grpc_core::RefCountedPtr<HealthCheckServiceImpl::WatchReactor> watcher) { |
|
|
|
|
grpc::internal::MutexLock lock(&mu_); |
|
|
|
|
std::shared_ptr<HealthCheckServiceImpl::CallHandler> handler) { |
|
|
|
|
grpc_core::MutexLock lock(&mu_); |
|
|
|
|
ServiceData& service_data = services_map_[service_name]; |
|
|
|
|
watcher->SendHealth(service_data.GetServingStatus()); |
|
|
|
|
service_data.AddWatch(std::move(watcher)); |
|
|
|
|
service_data.AddCallHandler(handler /* copies ref */); |
|
|
|
|
HealthCheckServiceImpl::CallHandler* h = handler.get(); |
|
|
|
|
h->SendHealth(std::move(handler), service_data.GetServingStatus()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::UnregisterWatch( |
|
|
|
|
void DefaultHealthCheckService::UnregisterCallHandler( |
|
|
|
|
const std::string& service_name, |
|
|
|
|
HealthCheckServiceImpl::WatchReactor* watcher) { |
|
|
|
|
grpc::internal::MutexLock lock(&mu_); |
|
|
|
|
const std::shared_ptr<HealthCheckServiceImpl::CallHandler>& handler) { |
|
|
|
|
grpc_core::MutexLock lock(&mu_); |
|
|
|
|
auto it = services_map_.find(service_name); |
|
|
|
|
if (it == services_map_.end()) return; |
|
|
|
|
ServiceData& service_data = it->second; |
|
|
|
|
service_data.RemoveWatch(watcher); |
|
|
|
|
if (service_data.Unused()) services_map_.erase(it); |
|
|
|
|
service_data.RemoveCallHandler(handler); |
|
|
|
|
if (service_data.Unused()) { |
|
|
|
|
services_map_.erase(it); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DefaultHealthCheckService::HealthCheckServiceImpl* |
|
|
|
|
DefaultHealthCheckService::GetHealthCheckService() { |
|
|
|
|
DefaultHealthCheckService::GetHealthCheckService( |
|
|
|
|
std::unique_ptr<ServerCompletionQueue> cq) { |
|
|
|
|
GPR_ASSERT(impl_ == nullptr); |
|
|
|
|
impl_ = absl::make_unique<HealthCheckServiceImpl>(this); |
|
|
|
|
impl_ = absl::make_unique<HealthCheckServiceImpl>(this, std::move(cq)); |
|
|
|
|
return impl_.get(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -122,19 +132,19 @@ DefaultHealthCheckService::GetHealthCheckService() { |
|
|
|
|
void DefaultHealthCheckService::ServiceData::SetServingStatus( |
|
|
|
|
ServingStatus status) { |
|
|
|
|
status_ = status; |
|
|
|
|
for (const auto& p : watchers_) { |
|
|
|
|
p.first->SendHealth(status); |
|
|
|
|
for (auto& call_handler : call_handlers_) { |
|
|
|
|
call_handler->SendHealth(call_handler /* copies ref */, status); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::ServiceData::AddWatch( |
|
|
|
|
grpc_core::RefCountedPtr<HealthCheckServiceImpl::WatchReactor> watcher) { |
|
|
|
|
watchers_[watcher.get()] = std::move(watcher); |
|
|
|
|
void DefaultHealthCheckService::ServiceData::AddCallHandler( |
|
|
|
|
std::shared_ptr<HealthCheckServiceImpl::CallHandler> handler) { |
|
|
|
|
call_handlers_.insert(std::move(handler)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::ServiceData::RemoveWatch( |
|
|
|
|
HealthCheckServiceImpl::WatchReactor* watcher) { |
|
|
|
|
watchers_.erase(watcher); |
|
|
|
|
void DefaultHealthCheckService::ServiceData::RemoveCallHandler( |
|
|
|
|
const std::shared_ptr<HealthCheckServiceImpl::CallHandler>& handler) { |
|
|
|
|
call_handlers_.erase(handler); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
@ -147,57 +157,53 @@ const char kHealthWatchMethodName[] = "/grpc.health.v1.Health/Watch"; |
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
DefaultHealthCheckService::HealthCheckServiceImpl::HealthCheckServiceImpl( |
|
|
|
|
DefaultHealthCheckService* database) |
|
|
|
|
: database_(database) { |
|
|
|
|
DefaultHealthCheckService* database, |
|
|
|
|
std::unique_ptr<ServerCompletionQueue> cq) |
|
|
|
|
: database_(database), cq_(std::move(cq)) { |
|
|
|
|
// Add Check() method.
|
|
|
|
|
AddMethod(new internal::RpcServiceMethod( |
|
|
|
|
kHealthCheckMethodName, internal::RpcMethod::NORMAL_RPC, nullptr)); |
|
|
|
|
MarkMethodCallback( |
|
|
|
|
0, new internal::CallbackUnaryHandler<ByteBuffer, ByteBuffer>( |
|
|
|
|
[database](CallbackServerContext* context, |
|
|
|
|
const ByteBuffer* request, ByteBuffer* response) { |
|
|
|
|
return HandleCheckRequest(database, context, request, response); |
|
|
|
|
})); |
|
|
|
|
// Add Watch() method.
|
|
|
|
|
AddMethod(new internal::RpcServiceMethod( |
|
|
|
|
kHealthWatchMethodName, internal::RpcMethod::SERVER_STREAMING, nullptr)); |
|
|
|
|
MarkMethodCallback( |
|
|
|
|
1, new internal::CallbackServerStreamingHandler<ByteBuffer, ByteBuffer>( |
|
|
|
|
[this](CallbackServerContext* /*ctx*/, const ByteBuffer* request) { |
|
|
|
|
return new WatchReactor(this, request); |
|
|
|
|
})); |
|
|
|
|
// Create serving thread.
|
|
|
|
|
thread_ = absl::make_unique<grpc_core::Thread>("grpc_health_check_service", |
|
|
|
|
Serve, this); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DefaultHealthCheckService::HealthCheckServiceImpl::~HealthCheckServiceImpl() { |
|
|
|
|
grpc::internal::MutexLock lock(&mu_); |
|
|
|
|
// We will reach here after the server starts shutting down.
|
|
|
|
|
shutdown_ = true; |
|
|
|
|
while (num_watches_ > 0) { |
|
|
|
|
shutdown_condition_.Wait(&mu_); |
|
|
|
|
{ |
|
|
|
|
grpc_core::MutexLock lock(&cq_shutdown_mu_); |
|
|
|
|
cq_->Shutdown(); |
|
|
|
|
} |
|
|
|
|
thread_->Join(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ServerUnaryReactor* |
|
|
|
|
DefaultHealthCheckService::HealthCheckServiceImpl::HandleCheckRequest( |
|
|
|
|
DefaultHealthCheckService* database, CallbackServerContext* context, |
|
|
|
|
const ByteBuffer* request, ByteBuffer* response) { |
|
|
|
|
auto* reactor = context->DefaultReactor(); |
|
|
|
|
std::string service_name; |
|
|
|
|
if (!DecodeRequest(*request, &service_name)) { |
|
|
|
|
reactor->Finish( |
|
|
|
|
Status(StatusCode::INVALID_ARGUMENT, "could not parse request")); |
|
|
|
|
return reactor; |
|
|
|
|
} |
|
|
|
|
ServingStatus serving_status = database->GetServingStatus(service_name); |
|
|
|
|
if (serving_status == NOT_FOUND) { |
|
|
|
|
reactor->Finish(Status(StatusCode::NOT_FOUND, "service name unknown")); |
|
|
|
|
return reactor; |
|
|
|
|
} |
|
|
|
|
if (!EncodeResponse(serving_status, response)) { |
|
|
|
|
reactor->Finish(Status(StatusCode::INTERNAL, "could not encode response")); |
|
|
|
|
return reactor; |
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::StartServingThread() { |
|
|
|
|
// Request the calls we're interested in.
|
|
|
|
|
// We do this before starting the serving thread, so that we know it's
|
|
|
|
|
// done before server startup is complete.
|
|
|
|
|
CheckCallHandler::CreateAndStart(cq_.get(), database_, this); |
|
|
|
|
WatchCallHandler::CreateAndStart(cq_.get(), database_, this); |
|
|
|
|
// Start serving thread.
|
|
|
|
|
thread_->Start(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::Serve(void* arg) { |
|
|
|
|
HealthCheckServiceImpl* service = static_cast<HealthCheckServiceImpl*>(arg); |
|
|
|
|
void* tag; |
|
|
|
|
bool ok; |
|
|
|
|
while (true) { |
|
|
|
|
if (!service->cq_->Next(&tag, &ok)) { |
|
|
|
|
// The completion queue is shutting down.
|
|
|
|
|
GPR_ASSERT(service->shutdown_); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
auto* next_step = static_cast<CallableTag*>(tag); |
|
|
|
|
next_step->Run(ok); |
|
|
|
|
} |
|
|
|
|
reactor->Finish(Status::OK); |
|
|
|
|
return reactor; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool DefaultHealthCheckService::HealthCheckServiceImpl::DecodeRequest( |
|
|
|
@ -248,124 +254,245 @@ bool DefaultHealthCheckService::HealthCheckServiceImpl::EncodeResponse( |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor
|
|
|
|
|
// DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor::WatchReactor( |
|
|
|
|
HealthCheckServiceImpl* service, const ByteBuffer* request) |
|
|
|
|
: service_(service) { |
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler:: |
|
|
|
|
CreateAndStart(ServerCompletionQueue* cq, |
|
|
|
|
DefaultHealthCheckService* database, |
|
|
|
|
HealthCheckServiceImpl* service) { |
|
|
|
|
std::shared_ptr<CallHandler> self = |
|
|
|
|
std::make_shared<CheckCallHandler>(cq, database, service); |
|
|
|
|
CheckCallHandler* handler = static_cast<CheckCallHandler*>(self.get()); |
|
|
|
|
{ |
|
|
|
|
grpc::internal::MutexLock lock(&service_->mu_); |
|
|
|
|
++service_->num_watches_; |
|
|
|
|
grpc_core::MutexLock lock(&service->cq_shutdown_mu_); |
|
|
|
|
if (service->shutdown_) return; |
|
|
|
|
// Request a Check() call.
|
|
|
|
|
handler->next_ = |
|
|
|
|
CallableTag(std::bind(&CheckCallHandler::OnCallReceived, handler, |
|
|
|
|
std::placeholders::_1, std::placeholders::_2), |
|
|
|
|
std::move(self)); |
|
|
|
|
service->RequestAsyncUnary(0, &handler->ctx_, &handler->request_, |
|
|
|
|
&handler->writer_, cq, cq, &handler->next_); |
|
|
|
|
} |
|
|
|
|
bool success = DecodeRequest(*request, &service_name_); |
|
|
|
|
gpr_log(GPR_DEBUG, "[HCS %p] watcher %p \"%s\": watch call started", service_, |
|
|
|
|
this, service_name_.c_str()); |
|
|
|
|
if (!success) { |
|
|
|
|
MaybeFinishLocked(Status(StatusCode::INTERNAL, "could not parse request")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler:: |
|
|
|
|
CheckCallHandler(ServerCompletionQueue* cq, |
|
|
|
|
DefaultHealthCheckService* database, |
|
|
|
|
HealthCheckServiceImpl* service) |
|
|
|
|
: cq_(cq), database_(database), service_(service), writer_(&ctx_) {} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler:: |
|
|
|
|
OnCallReceived(std::shared_ptr<CallHandler> self, bool ok) { |
|
|
|
|
if (!ok) { |
|
|
|
|
// The value of ok being false means that the server is shutting down.
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
// Register the call for updates to the service.
|
|
|
|
|
service_->database_->RegisterWatch(service_name_, Ref()); |
|
|
|
|
// Spawn a new handler instance to serve the next new client. Every handler
|
|
|
|
|
// instance will deallocate itself when it's done.
|
|
|
|
|
CreateAndStart(cq_, database_, service_); |
|
|
|
|
// Process request.
|
|
|
|
|
gpr_log(GPR_DEBUG, "[HCS %p] Health check started for handler %p", service_, |
|
|
|
|
this); |
|
|
|
|
std::string service_name; |
|
|
|
|
grpc::Status status = Status::OK; |
|
|
|
|
ByteBuffer response; |
|
|
|
|
if (!service_->DecodeRequest(request_, &service_name)) { |
|
|
|
|
status = Status(StatusCode::INVALID_ARGUMENT, "could not parse request"); |
|
|
|
|
} else { |
|
|
|
|
ServingStatus serving_status = database_->GetServingStatus(service_name); |
|
|
|
|
if (serving_status == NOT_FOUND) { |
|
|
|
|
status = Status(StatusCode::NOT_FOUND, "service name unknown"); |
|
|
|
|
} else if (!service_->EncodeResponse(serving_status, &response)) { |
|
|
|
|
status = Status(StatusCode::INTERNAL, "could not encode response"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Send response.
|
|
|
|
|
{ |
|
|
|
|
grpc_core::MutexLock lock(&service_->cq_shutdown_mu_); |
|
|
|
|
if (!service_->shutdown_) { |
|
|
|
|
next_ = |
|
|
|
|
CallableTag(std::bind(&CheckCallHandler::OnFinishDone, this, |
|
|
|
|
std::placeholders::_1, std::placeholders::_2), |
|
|
|
|
std::move(self)); |
|
|
|
|
if (status.ok()) { |
|
|
|
|
writer_.Finish(response, status, &next_); |
|
|
|
|
} else { |
|
|
|
|
writer_.FinishWithError(status, &next_); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor:: |
|
|
|
|
SendHealth(ServingStatus status) { |
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler:: |
|
|
|
|
OnFinishDone(std::shared_ptr<CallHandler> self, bool ok) { |
|
|
|
|
if (ok) { |
|
|
|
|
gpr_log(GPR_DEBUG, "[HCS %p] Health check call finished for handler %p", |
|
|
|
|
service_, this); |
|
|
|
|
} |
|
|
|
|
self.reset(); // To appease clang-tidy.
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: |
|
|
|
|
CreateAndStart(ServerCompletionQueue* cq, |
|
|
|
|
DefaultHealthCheckService* database, |
|
|
|
|
HealthCheckServiceImpl* service) { |
|
|
|
|
std::shared_ptr<CallHandler> self = |
|
|
|
|
std::make_shared<WatchCallHandler>(cq, database, service); |
|
|
|
|
WatchCallHandler* handler = static_cast<WatchCallHandler*>(self.get()); |
|
|
|
|
{ |
|
|
|
|
grpc_core::MutexLock lock(&service->cq_shutdown_mu_); |
|
|
|
|
if (service->shutdown_) return; |
|
|
|
|
// Request AsyncNotifyWhenDone().
|
|
|
|
|
handler->on_done_notified_ = |
|
|
|
|
CallableTag(std::bind(&WatchCallHandler::OnDoneNotified, handler, |
|
|
|
|
std::placeholders::_1, std::placeholders::_2), |
|
|
|
|
self /* copies ref */); |
|
|
|
|
handler->ctx_.AsyncNotifyWhenDone(&handler->on_done_notified_); |
|
|
|
|
// Request a Watch() call.
|
|
|
|
|
handler->next_ = |
|
|
|
|
CallableTag(std::bind(&WatchCallHandler::OnCallReceived, handler, |
|
|
|
|
std::placeholders::_1, std::placeholders::_2), |
|
|
|
|
std::move(self)); |
|
|
|
|
service->RequestAsyncServerStreaming(1, &handler->ctx_, &handler->request_, |
|
|
|
|
&handler->stream_, cq, cq, |
|
|
|
|
&handler->next_); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: |
|
|
|
|
WatchCallHandler(ServerCompletionQueue* cq, |
|
|
|
|
DefaultHealthCheckService* database, |
|
|
|
|
HealthCheckServiceImpl* service) |
|
|
|
|
: cq_(cq), database_(database), service_(service), stream_(&ctx_) {} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: |
|
|
|
|
OnCallReceived(std::shared_ptr<CallHandler> self, bool ok) { |
|
|
|
|
if (!ok) { |
|
|
|
|
// Server shutting down.
|
|
|
|
|
//
|
|
|
|
|
// AsyncNotifyWhenDone() needs to be called before the call starts, but the
|
|
|
|
|
// tag will not pop out if the call never starts (
|
|
|
|
|
// https://github.com/grpc/grpc/issues/10136). So we need to manually
|
|
|
|
|
// release the ownership of the handler in this case.
|
|
|
|
|
GPR_ASSERT(on_done_notified_.ReleaseHandler() != nullptr); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
// Spawn a new handler instance to serve the next new client. Every handler
|
|
|
|
|
// instance will deallocate itself when it's done.
|
|
|
|
|
CreateAndStart(cq_, database_, service_); |
|
|
|
|
// Parse request.
|
|
|
|
|
if (!service_->DecodeRequest(request_, &service_name_)) { |
|
|
|
|
SendFinish(std::move(self), |
|
|
|
|
Status(StatusCode::INVALID_ARGUMENT, "could not parse request")); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
// Register the call for updates to the service.
|
|
|
|
|
gpr_log(GPR_DEBUG, |
|
|
|
|
"[HCS %p] watcher %p \"%s\": SendHealth() for ServingStatus %d", |
|
|
|
|
service_, this, service_name_.c_str(), status); |
|
|
|
|
grpc::internal::MutexLock lock(&mu_); |
|
|
|
|
"[HCS %p] Health watch started for service \"%s\" (handler: %p)", |
|
|
|
|
service_, service_name_.c_str(), this); |
|
|
|
|
database_->RegisterCallHandler(service_name_, std::move(self)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: |
|
|
|
|
SendHealth(std::shared_ptr<CallHandler> self, ServingStatus status) { |
|
|
|
|
grpc_core::MutexLock lock(&send_mu_); |
|
|
|
|
// If there's already a send in flight, cache the new status, and
|
|
|
|
|
// we'll start a new send for it when the one in flight completes.
|
|
|
|
|
if (write_pending_) { |
|
|
|
|
gpr_log(GPR_DEBUG, "[HCS %p] watcher %p \"%s\": queuing write", service_, |
|
|
|
|
this, service_name_.c_str()); |
|
|
|
|
if (send_in_flight_) { |
|
|
|
|
pending_status_ = status; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
// Start a send.
|
|
|
|
|
SendHealthLocked(status); |
|
|
|
|
SendHealthLocked(std::move(self), status); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor:: |
|
|
|
|
SendHealthLocked(ServingStatus status) { |
|
|
|
|
// Do nothing if Finish() has already been called.
|
|
|
|
|
if (finish_called_) return; |
|
|
|
|
// Check if we're shutting down.
|
|
|
|
|
{ |
|
|
|
|
grpc::internal::MutexLock lock(&service_->mu_); |
|
|
|
|
if (service_->shutdown_) { |
|
|
|
|
MaybeFinishLocked( |
|
|
|
|
Status(StatusCode::CANCELLED, "not writing due to shutdown")); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: |
|
|
|
|
SendHealthLocked(std::shared_ptr<CallHandler> self, ServingStatus status) { |
|
|
|
|
send_in_flight_ = true; |
|
|
|
|
// Construct response.
|
|
|
|
|
ByteBuffer response; |
|
|
|
|
bool success = service_->EncodeResponse(status, &response); |
|
|
|
|
// Grab shutdown lock and send response.
|
|
|
|
|
grpc_core::MutexLock cq_lock(&service_->cq_shutdown_mu_); |
|
|
|
|
if (service_->shutdown_) { |
|
|
|
|
SendFinishLocked(std::move(self), Status::CANCELLED); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
// Send response.
|
|
|
|
|
bool success = EncodeResponse(status, &response_); |
|
|
|
|
if (!success) { |
|
|
|
|
MaybeFinishLocked( |
|
|
|
|
Status(StatusCode::INTERNAL, "could not encode response")); |
|
|
|
|
SendFinishLocked(std::move(self), |
|
|
|
|
Status(StatusCode::INTERNAL, "could not encode response")); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
gpr_log(GPR_DEBUG, |
|
|
|
|
"[HCS %p] watcher %p \"%s\": starting write for ServingStatus %d", |
|
|
|
|
service_, this, service_name_.c_str(), status); |
|
|
|
|
write_pending_ = true; |
|
|
|
|
StartWrite(&response_); |
|
|
|
|
next_ = CallableTag(std::bind(&WatchCallHandler::OnSendHealthDone, this, |
|
|
|
|
std::placeholders::_1, std::placeholders::_2), |
|
|
|
|
std::move(self)); |
|
|
|
|
stream_.Write(response, &next_); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor:: |
|
|
|
|
OnWriteDone(bool ok) { |
|
|
|
|
gpr_log(GPR_DEBUG, "[HCS %p] watcher %p \"%s\": OnWriteDone(): ok=%d", |
|
|
|
|
service_, this, service_name_.c_str(), ok); |
|
|
|
|
response_.Clear(); |
|
|
|
|
grpc::internal::MutexLock lock(&mu_); |
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: |
|
|
|
|
OnSendHealthDone(std::shared_ptr<CallHandler> self, bool ok) { |
|
|
|
|
if (!ok) { |
|
|
|
|
MaybeFinishLocked(Status(StatusCode::CANCELLED, "OnWriteDone() ok=false")); |
|
|
|
|
SendFinish(std::move(self), Status::CANCELLED); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
write_pending_ = false; |
|
|
|
|
grpc_core::MutexLock lock(&send_mu_); |
|
|
|
|
send_in_flight_ = false; |
|
|
|
|
// If we got a new status since we started the last send, start a
|
|
|
|
|
// new send for it.
|
|
|
|
|
if (pending_status_ != NOT_FOUND) { |
|
|
|
|
auto status = pending_status_; |
|
|
|
|
pending_status_ = NOT_FOUND; |
|
|
|
|
SendHealthLocked(status); |
|
|
|
|
SendHealthLocked(std::move(self), status); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor:: |
|
|
|
|
OnCancel() { |
|
|
|
|
grpc::internal::MutexLock lock(&mu_); |
|
|
|
|
MaybeFinishLocked(Status(StatusCode::UNKNOWN, "OnCancel()")); |
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: |
|
|
|
|
SendFinish(std::shared_ptr<CallHandler> self, const Status& status) { |
|
|
|
|
if (finish_called_) return; |
|
|
|
|
grpc_core::MutexLock cq_lock(&service_->cq_shutdown_mu_); |
|
|
|
|
if (service_->shutdown_) return; |
|
|
|
|
SendFinishLocked(std::move(self), status); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor::OnDone() { |
|
|
|
|
gpr_log(GPR_DEBUG, "[HCS %p] watcher %p \"%s\": OnDone()", service_, this, |
|
|
|
|
service_name_.c_str()); |
|
|
|
|
service_->database_->UnregisterWatch(service_name_, this); |
|
|
|
|
{ |
|
|
|
|
grpc::internal::MutexLock lock(&service_->mu_); |
|
|
|
|
if (--service_->num_watches_ == 0 && service_->shutdown_) { |
|
|
|
|
service_->shutdown_condition_.Signal(); |
|
|
|
|
} |
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: |
|
|
|
|
SendFinishLocked(std::shared_ptr<CallHandler> self, const Status& status) { |
|
|
|
|
on_finish_done_ = |
|
|
|
|
CallableTag(std::bind(&WatchCallHandler::OnFinishDone, this, |
|
|
|
|
std::placeholders::_1, std::placeholders::_2), |
|
|
|
|
std::move(self)); |
|
|
|
|
stream_.Finish(status, &on_finish_done_); |
|
|
|
|
finish_called_ = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: |
|
|
|
|
OnFinishDone(std::shared_ptr<CallHandler> self, bool ok) { |
|
|
|
|
if (ok) { |
|
|
|
|
gpr_log(GPR_DEBUG, |
|
|
|
|
"[HCS %p] Health watch call finished (service_name: \"%s\", " |
|
|
|
|
"handler: %p).", |
|
|
|
|
service_, service_name_.c_str(), this); |
|
|
|
|
} |
|
|
|
|
// Free the initial ref from instantiation.
|
|
|
|
|
Unref(); |
|
|
|
|
self.reset(); // To appease clang-tidy.
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchReactor:: |
|
|
|
|
MaybeFinishLocked(Status status) { |
|
|
|
|
// TODO(roth): This method currently assumes that there will be only one
|
|
|
|
|
// thread polling the cq and invoking the corresponding callbacks. If
|
|
|
|
|
// that changes, we will need to add synchronization here.
|
|
|
|
|
void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: |
|
|
|
|
OnDoneNotified(std::shared_ptr<CallHandler> self, bool ok) { |
|
|
|
|
GPR_ASSERT(ok); |
|
|
|
|
gpr_log(GPR_DEBUG, |
|
|
|
|
"[HCS %p] watcher %p \"%s\": MaybeFinishLocked() with code=%d msg=%s", |
|
|
|
|
service_, this, service_name_.c_str(), status.error_code(), |
|
|
|
|
status.error_message().c_str()); |
|
|
|
|
if (!finish_called_) { |
|
|
|
|
gpr_log(GPR_DEBUG, "[HCS %p] watcher %p \"%s\": actually calling Finish()", |
|
|
|
|
service_, this, service_name_.c_str()); |
|
|
|
|
finish_called_ = true; |
|
|
|
|
Finish(status); |
|
|
|
|
} |
|
|
|
|
"[HCS %p] Health watch call is notified done (handler: %p, " |
|
|
|
|
"is_cancelled: %d).", |
|
|
|
|
service_, this, static_cast<int>(ctx_.IsCancelled())); |
|
|
|
|
database_->UnregisterCallHandler(service_name_, self); |
|
|
|
|
SendFinish(std::move(self), Status::CANCELLED); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace grpc
|
|
|
|
|