/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright 2020 Couchbase, Inc. * * 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 #include #include namespace couchbase::io { class http_session_manager : public std::enable_shared_from_this { public: http_session_manager(const std::string& client_id, asio::io_context& ctx) : client_id_(client_id) , ctx_(ctx) { } void set_configuration(const configuration& config) { config_ = config; next_index_ = 0; if (config_.nodes.size() > 1) { std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution dis(0, config_.nodes.size() - 1); next_index_ = dis(gen); } } std::shared_ptr check_out(service_type type, const std::string& username, const std::string& password) { if (idle_sessions_[type].empty()) { std::string hostname; std::uint16_t port = 0; std::tie(hostname, port) = next_node(type); if (port == 0) { return nullptr; } config_.nodes.size(); auto session = std::make_shared(client_id_, ctx_, username, password, hostname, std::to_string(port)); session->on_stop([type, id = session->id(), self = this->shared_from_this()]() { self->busy_sessions_[type].remove_if([id](const auto& s) -> bool { return s->id() == id; }); self->idle_sessions_[type].remove_if([id](const auto& s) -> bool { return s->id() == id; }); }); busy_sessions_[type].push_back(session); return session; } auto session = idle_sessions_[type].front(); idle_sessions_[type].pop_front(); busy_sessions_[type].push_back(session); return session; } void check_in(service_type type, std::shared_ptr session) { idle_sessions_[type].push_back(session); } private: std::pair next_node(service_type type) { auto candidates = config_.nodes.size(); while (candidates > 0) { --candidates; auto& node = config_.nodes[next_index_]; next_index_ = (next_index_ + 1) % config_.nodes.size(); std::uint16_t port = 0; switch (type) { case service_type::query: port = node.services_plain.query.value_or(0); break; case service_type::analytics: port = node.services_plain.analytics.value_or(0); break; case service_type::search: port = node.services_plain.search.value_or(0); break; case service_type::views: port = node.services_plain.views.value_or(0); break; case service_type::management: port = node.services_plain.management.value_or(0); break; case service_type::kv: port = node.services_plain.key_value.value_or(0); break; } if (port != 0) { return { node.hostname, port }; } } return { "", 0 }; } std::string client_id_; asio::io_context& ctx_; configuration config_{}; std::map>> busy_sessions_{}; std::map>> idle_sessions_{}; std::size_t next_index_{ 0 }; }; } // namespace couchbase::io