#pragma once #include #include #include #include #include #include #include #include #include #include "IServer.h" #include #include #include #include #include #include "GrpcConnection.grpc.pb.h" #include "WriteBufferFromGRPC.h" using GRPCConnection::QueryRequest; using GRPCConnection::QueryResponse; using GRPCConnection::GRPC; namespace DB { class CommonCallData { public: GRPC::AsyncService* service; grpc::ServerCompletionQueue* notification_cq; grpc::ServerCompletionQueue* new_call_cq; grpc::ServerContext gRPCcontext; IServer* iServer; bool with_stacktrace = false; Poco::Logger * log; std::unique_ptr next_client; public: explicit CommonCallData(GRPC::AsyncService* Service_, grpc::ServerCompletionQueue* notification_cq_, grpc::ServerCompletionQueue* new_call_cq_, IServer* iServer_, Poco::Logger * log_) : service(Service_), notification_cq(notification_cq_), new_call_cq(new_call_cq_), iServer(iServer_), log(log_) {} virtual ~CommonCallData() {} virtual void respond() = 0; }; class CallDataQuery : public CommonCallData { public: CallDataQuery(GRPC::AsyncService* Service_, grpc::ServerCompletionQueue* notification_cq_, grpc::ServerCompletionQueue* new_call_cq_, IServer* server_, Poco::Logger * log_) : CommonCallData(Service_, notification_cq_, new_call_cq_, server_, log_), responder(&gRPCcontext), context(iServer->context()) { detailsStatus = SEND_TOTALS; status = START_QUERY; out = std::make_shared(&responder, (void*)this, nullptr); service->RequestQuery(&gRPCcontext, &responder, new_call_cq, notification_cq, this); } void ParseQuery(); void ParseData(); void ReadData(); void ExecuteQuery(); void ProgressQuery(); void FinishQuery(); enum DetailsStatus { SEND_TOTALS, SEND_EXTREMES, SEND_PROFILEINFO, FINISH }; void SendDetails(); bool sendData(const Block & block); bool sendProgress(); bool sendTotals(const Block & totals); bool sendExtremes(const Block & block); enum Status { START_QUERY, PARSE_QUERY, READ_DATA, PROGRESS, FINISH_QUERY }; virtual void respond() override; virtual ~CallDataQuery() override { query_watch.stop(); progress_watch.stop(); query_context.reset(); query_scope.reset(); } private: QueryRequest request; QueryResponse response; grpc::ServerAsyncReaderWriter responder; Stopwatch progress_watch; Stopwatch query_watch; Progress progress; DetailsStatus detailsStatus; Status status; BlockIO io; Context context; std::shared_ptr executor; std::optional query_context; std::shared_ptr out; String format_output; String format_input; uint64_t interactive_delay; std::optional query_scope; }; class GRPCServer final : public Poco::Runnable { public: GRPCServer(const GRPCServer &handler) = delete; GRPCServer(GRPCServer &&handler) = delete; GRPCServer(std::string server_address_, IServer & server_) : iServer(server_), log(&Poco::Logger::get("GRPCHandler")) { grpc::ServerBuilder builder; builder.AddListeningPort(server_address_, grpc::InsecureServerCredentials()); //keepalive pings default values builder.RegisterService(&service); builder.SetMaxReceiveMessageSize(INT_MAX); notification_cq = builder.AddCompletionQueue(); new_call_cq = builder.AddCompletionQueue(); server = builder.BuildAndStart(); } void stop() { server->Shutdown(); notification_cq->Shutdown(); new_call_cq->Shutdown(); } virtual void run() override { HandleRpcs(); } void HandleRpcs() { new CallDataQuery(&service, notification_cq.get(), new_call_cq.get(), &iServer, log); // rpc event "read done / write done / close(already connected)" call-back by this completion queue auto handle_calls_completion = [&]() { void* tag; bool ok; while (true) { GPR_ASSERT(new_call_cq->Next(&tag, &ok)); if (!ok) { LOG_WARNING(log, "Client has gone away."); delete static_cast(tag); continue; } auto thread = ThreadFromGlobalPool{&CallDataQuery::respond, static_cast(tag)}; thread.detach(); } }; // rpc event "new connection / close(waiting for connect)" call-back by this completion queue auto handle_requests_completion = [&] { void* tag; bool ok; while (true) { GPR_ASSERT(notification_cq->Next(&tag, &ok)); if (!ok) { LOG_WARNING(log, "Client has gone away."); delete static_cast(tag); continue; } auto thread = ThreadFromGlobalPool{&CallDataQuery::respond, static_cast(tag)}; thread.detach(); } }; auto notification_cq_thread = ThreadFromGlobalPool{handle_requests_completion}; auto new_call_cq_thread = ThreadFromGlobalPool{handle_calls_completion}; notification_cq_thread.detach(); new_call_cq_thread.detach(); } private: IServer & iServer; Poco::Logger * log; std::unique_ptr notification_cq; std::unique_ptr new_call_cq; GRPC::AsyncService service; std::unique_ptr server; std::string server_address; }; }