// Copyright 2022 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // License, and you may not use this file except in compliance with the Business Source License. // // As of the Change Date specified in that file, in accordance with // the Business Source License, use of this software will be governed // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include "communication/context.hpp" #include "communication/init.hpp" #include "communication/v2/listener.hpp" #include "communication/v2/pool.hpp" #include "utils/logging.hpp" #include "utils/message.hpp" #include "utils/thread.hpp" namespace memgraph::communication::v2 { using Socket = boost::asio::ip::tcp::socket; using ServerEndpoint = boost::asio::ip::tcp::endpoint; /** * Communication server. * * Listens for incoming connections on the server port and assigns them to the * connection listener. The listener and session are implemented using asio * async model. Currently the implemented model is thread per core model * opposed to io_context per core. The reasoning for opting for the former model * is the robustness to the multiple resource demanding queries that can be split * across multiple threads, and then a single thread would not block io_context, * unlike in the latter model where it is possible that thread that accepts * request is being blocked by demanding query. * All logic is contained within handlers that are being dispatched * on a single strand per session. The only exception is write which is * synchronous since the nature of the clients conenction is synchronous as * well. * * Current Server architecture: * incoming connection -> server -> listener -> session * * @tparam TSession the server can handle different Sessions, each session * represents a different protocol so the same network infrastructure * can be used for handling different protocols * @tparam TSessionData the class with objects that will be forwarded to the * session */ template class Server final { using ServerHandler = Server; public: /** * Constructs and binds server to endpoint, operates on session data and * invokes workers_count workers */ Server(ServerEndpoint &endpoint, TSessionData *session_data, ServerContext *server_context, const int inactivity_timeout_sec, const std::string_view service_name, size_t workers_count = std::thread::hardware_concurrency()) : endpoint_{endpoint}, service_name_{service_name}, context_thread_pool_{workers_count}, listener_{Listener::Create(context_thread_pool_.GetIOContext(), session_data, server_context, endpoint_, service_name_, inactivity_timeout_sec)} {} ~Server() { MG_ASSERT(!IsRunning(), "Server wasn't shutdown properly"); } Server(const Server &) = delete; Server(Server &&) = delete; Server &operator=(const Server &) = delete; Server &operator=(Server &&) = delete; const auto &Endpoint() const { MG_ASSERT(IsRunning(), "You can't get the server endpoint when it's not running!"); return endpoint_; } bool Start() { if (IsRunning()) { spdlog::error("The server is already running"); return false; } listener_->Start(); spdlog::info("{} server is fully armed and operational", service_name_); spdlog::info("{} listening on {}", service_name_, endpoint_.address()); context_thread_pool_.Run(); return true; } void Shutdown() { context_thread_pool_.Shutdown(); spdlog::info("{} shutting down...", service_name_); } void AwaitShutdown() { context_thread_pool_.AwaitShutdown(); } bool IsRunning() const noexcept { return context_thread_pool_.IsRunning() && listener_->IsRunning(); } private: ServerEndpoint endpoint_; std::string service_name_; IOContextThreadPool context_thread_pool_; std::shared_ptr> listener_; }; } // namespace memgraph::communication::v2