Skip to content

Commit

Permalink
infra: improve gRPC server error if a port is already in use (#1398) (#…
Browse files Browse the repository at this point in the history
…2500)

Implement gRPC server "global callbacks" to check port conflicts.
  • Loading branch information
amitrahman1026 authored Nov 15, 2024
1 parent 075baf0 commit cd976ba
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 2 deletions.
15 changes: 13 additions & 2 deletions silkworm/infra/grpc/server/server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include <silkworm/infra/grpc/server/server_context_pool.hpp>
#include <silkworm/infra/grpc/server/server_settings.hpp>

#include "silkworm/infra/grpc/server/server_callbacks.hpp"

namespace silkworm::rpc {

//! Base RPC server able to serve incoming requests for gRPC \ref AsyncService instances.
Expand Down Expand Up @@ -78,10 +80,16 @@ class Server {
register_async_services(builder);

server_ = builder.BuildAndStart();
SILK_TRACE << "Server " << this << " bound at selected port: " << selected_port;

if (server_ == nullptr) {
std::string error_msg = "cannot start gRPC server at " + settings_.address_uri;

if (ServerGlobalCallbacks::check_and_clear_bad_port_error()) {
error_msg += " (port already in use)";
}

SILK_ERROR << "Server " << this << " BuildAndStart failed [" << settings_.address_uri << "]";
throw std::runtime_error("cannot start gRPC server at " + settings_.address_uri);
throw std::runtime_error(error_msg);
}

// gRPC async model requires the server to register one request call for each RPC in advance.
Expand Down Expand Up @@ -174,6 +182,9 @@ class Server {
//! The gRPC server instance tied to this Server lifetime.
std::unique_ptr<grpc::Server> server_;

//! The global callbacks are shared between all instances of Servers
static inline const ServerGlobalCallbacks kGlobalCallbacks;

//! Pool of server schedulers used to run the execution loops.
std::unique_ptr<ServerContextPool> context_pool_;

Expand Down
62 changes: 62 additions & 0 deletions silkworm/infra/grpc/server/server_callbacks.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
Copyright 2024 The Silkworm Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#pragma once

#include <atomic>
#include <mutex>

#include <grpcpp/grpcpp.h>

#include <silkworm/infra/common/log.hpp>

namespace silkworm::rpc {

class ServerGlobalCallbacks {
public:
ServerGlobalCallbacks() {
// NOTE: Despite its documentation, SetGlobalCallbacks() does take the ownership
// of the object pointer. So we just "new" and let underlying GRPC manage its lifetime.
static std::once_flag callback_init_flag;
std::call_once(callback_init_flag, []() {
grpc::Server::SetGlobalCallbacks(new Callbacks());
});
}

static bool check_and_clear_bad_port_error() {
return bad_port_error.exchange(false);
}

private:
static inline std::atomic<bool> bad_port_error{false};
class Callbacks final : public grpc::Server::GlobalCallbacks {
public:
Callbacks() = default;
~Callbacks() override = default;

void PreSynchronousRequest(grpc::ServerContext*) override{};
void PostSynchronousRequest(grpc::ServerContext*) override{};

void AddPort(grpc::Server*, const std::string&,
grpc::ServerCredentials*, int port) override {
if (port == 0) {
ServerGlobalCallbacks::bad_port_error.store(true, std::memory_order_release);
}
}
};
};

} // namespace silkworm::rpc

0 comments on commit cd976ba

Please sign in to comment.