/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright 2020-2021 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. */ #include "couchbase/build_config.hxx" #include "cluster.hxx" #include "bucket.hxx" #include "capella_ca.hxx" #include "core/impl/get_replica.hxx" #include "core/impl/lookup_in_replica.hxx" #include "core/impl/observe_seqno.hxx" #include "core/io/http_command.hxx" #include "core/io/http_session_manager.hxx" #include "core/io/mcbp_command.hxx" #include "core/io/mcbp_session.hxx" #include "core/management/analytics_link.hxx" #include "core/mcbp/completion_token.hxx" #include "core/mcbp/queue_request.hxx" #include "core/metrics/logging_meter.hxx" #include "core/metrics/noop_meter.hxx" #include "core/operations.hxx" #include "core/operations/management/analytics.hxx" #include "core/operations/management/bucket.hxx" #include "core/operations/management/bucket_describe.hxx" #include "core/operations/management/cluster_describe.hxx" #include "core/operations/management/cluster_developer_preview_enable.hxx" #include "core/operations/management/collections.hxx" #include "core/operations/management/eventing.hxx" #include "core/operations/management/freeform.hxx" #include "core/operations/management/query.hxx" #include "core/operations/management/search.hxx" #include "core/operations/management/user.hxx" #include "core/operations/management/view.hxx" #include "core/tracing/noop_tracer.hxx" #include "core/tracing/threshold_logging_tracer.hxx" #include "core/utils/join_strings.hxx" #include "crud_component.hxx" #include "dispatcher.hxx" #include "impl/dns_srv_tracker.hxx" #include "mozilla_ca_bundle.hxx" #include "ping_collector.hxx" #include "ping_reporter.hxx" #include #include #include #include namespace couchbase::core { class ping_collector_impl : public std::enable_shared_from_this , public diag::ping_reporter , public diag::ping_collector { diag::ping_result res_; utils::movable_function handler_; std::atomic_int expected_{ 0 }; std::mutex mutex_{}; public: ping_collector_impl(std::string report_id, utils::movable_function&& handler) : res_{ std::move(report_id), meta::sdk_id() } , handler_(std::move(handler)) { } ~ping_collector_impl() override { invoke_handler(); } [[nodiscard]] diag::ping_result& result() { return res_; } void report(diag::endpoint_ping_info&& info) override { const std::scoped_lock lock(mutex_); res_.services[info.type].emplace_back(std::move(info)); if (--expected_ == 0) { invoke_handler(); } } auto build_reporter() -> std::shared_ptr override { ++expected_; return shared_from_this(); } void invoke_handler() { if (handler_ != nullptr) { handler_(std::move(res_)); handler_ = nullptr; } } }; template constexpr bool is_feature_supported(const Request& /* request */, const configuration_capabilities& /* capabilities */) { return true; } constexpr bool is_feature_supported(const operations::search_request& request, const configuration_capabilities& capabilities) { if (request.scope_name && !capabilities.supports_scoped_search_indexes()) { return false; } if (request.vector_search && !capabilities.supports_vector_search()) { return false; } return true; } class cluster_impl : public std::enable_shared_from_this { public: explicit cluster_impl(asio::io_context& ctx) : ctx_(ctx) , work_(asio::make_work_guard(ctx_)) , session_manager_(std::make_shared(id_, ctx_, tls_)) { } auto io_context() -> asio::io_context& { return ctx_; } void configure_tls_options(bool has_capella_host) { asio::ssl::context::options tls_options = asio::ssl::context::default_workarounds | // various bug workarounds that should be rather harmless asio::ssl::context::no_sslv2 | // published: 1995, deprecated: 2011 asio::ssl::context::no_sslv3; // published: 1996, deprecated: 2015 if (origin_.options().tls_disable_deprecated_protocols) { tls_options |= asio::ssl::context::no_tlsv1 | // published: 1999, deprecated: 2021 asio::ssl::context::no_tlsv1_1; // published: 2006, deprecated: 2021 } if (origin_.options().tls_disable_v1_2 || has_capella_host) { tls_options |= asio::ssl::context::no_tlsv1_2; // published: 2008, still in use } tls_.set_options(tls_options); switch (origin_.options().tls_verify) { case tls_verify_mode::none: tls_.set_verify_mode(asio::ssl::verify_none); break; case tls_verify_mode::peer: tls_.set_verify_mode(asio::ssl::verify_peer); break; } #ifdef COUCHBASE_CXX_CLIENT_TLS_KEY_LOG_FILE SSL_CTX_set_keylog_callback(tls_.native_handle(), [](const SSL* /* ssl */, const char* line) { std::ofstream keylog(COUCHBASE_CXX_CLIENT_TLS_KEY_LOG_FILE, std::ios::out | std::ios::app | std::ios::binary); keylog << std::string_view(line) << std::endl; }); CB_LOG_CRITICAL( "COUCHBASE_CXX_CLIENT_TLS_KEY_LOG_FILE was set to \"{}\" during build, all TLS keys will be logged for network analysis " "(https://wiki.wireshark.org/TLS). DO NOT USE THIS BUILD IN PRODUCTION", COUCHBASE_CXX_CLIENT_TLS_KEY_LOG_FILE); #endif } void open(couchbase::core::origin origin, utils::movable_function&& handler) { if (stopped_) { return handler(errc::network::cluster_closed); } if (origin.get_nodes().empty()) { stopped_ = true; work_.reset(); return handler(errc::common::invalid_argument); } origin_ = std::move(origin); CB_LOG_DEBUG(R"(open cluster, id: "{}", core version: "{}", {})", id_, couchbase::core::meta::sdk_semver(), origin_.to_json()); // ignore the enable_tracing flag if a tracer was passed in if (nullptr != origin_.options().tracer) { tracer_ = origin_.options().tracer; } else { if (origin_.options().enable_tracing) { tracer_ = std::make_shared(ctx_, origin_.options().tracing_options); } else { tracer_ = std::make_shared(); } } tracer_->start(); // ignore the metrics options if a meter was passed in. if (nullptr != origin_.options().meter) { meter_ = origin_.options().meter; } else { if (origin_.options().enable_metrics) { meter_ = std::make_shared(ctx_, origin_.options().metrics_options); } else { meter_ = std::make_shared(); } } meter_->start(); session_manager_->set_tracer(tracer_); if (origin_.options().enable_dns_srv) { std::string hostname; std::string port; std::tie(hostname, port) = origin_.next_address(); dns_srv_tracker_ = std::make_shared(ctx_, hostname, origin_.options().dns_config, origin_.options().enable_tls); return asio::post(asio::bind_executor( ctx_, [self = shared_from_this(), hostname = std::move(hostname), handler = std::move(handler)]() mutable { return self->dns_srv_tracker_->get_srv_nodes([self, hostname = std::move(hostname), handler = std::move(handler)]( origin::node_list nodes, std::error_code ec) mutable { if (ec) { return self->close([ec, handler = std::move(handler)]() mutable { handler(ec); }); } if (!nodes.empty()) { self->origin_.set_nodes(std::move(nodes)); CB_LOG_INFO("replace list of bootstrap nodes with addresses from DNS SRV of \"{}\": [{}]", hostname, utils::join_strings(self->origin_.get_nodes(), ", ")); } return self->do_open(std::move(handler)); }); })); } do_open(std::move(handler)); } void open_bucket(const std::string& bucket_name, utils::movable_function&& handler) { if (stopped_) { return handler(errc::network::cluster_closed); } std::shared_ptr b{}; { std::scoped_lock lock(buckets_mutex_); auto ptr = buckets_.find(bucket_name); if (ptr == buckets_.end()) { std::vector known_features; auto origin = origin_; if (session_ && session_->has_config()) { known_features = session_->supported_features(); origin = { origin_, session_->config().value() }; } b = std::make_shared(id_, ctx_, tls_, tracer_, meter_, bucket_name, origin, known_features, dns_srv_tracker_); buckets_.try_emplace(bucket_name, b); } } if (b == nullptr) { return handler({}); } b->on_configuration_update(session_manager_); b->bootstrap([self = shared_from_this(), bucket_name, handler = std::move(handler)](std::error_code ec, const topology::configuration& config) mutable { if (ec) { std::scoped_lock lock(self->buckets_mutex_); self->buckets_.erase(bucket_name); } else if (self->session_ && !self->session_->supports_gcccp()) { self->session_manager_->set_configuration(config, self->origin_.options()); } handler(ec); }); } void close_bucket(const std::string& bucket_name, utils::movable_function&& handler) { if (stopped_) { return handler(errc::network::cluster_closed); } std::shared_ptr b{}; { std::scoped_lock lock(buckets_mutex_); if (auto ptr = buckets_.find(bucket_name); ptr != buckets_.end()) { b = std::move(ptr->second); buckets_.erase(ptr); } } if (b != nullptr) { b->close(); } return handler({}); } std::pair origin() const { if (stopped_) { return { errc::network::cluster_closed, {} }; } return { {}, origin_ }; } template, int> = 0> void execute(Request request, Handler&& handler) { using response_type = typename Request::encoded_response_type; if (stopped_) { return handler(request.make_response(make_key_value_error_context(errc::network::cluster_closed, request.id), response_type{})); } if (auto bucket = find_bucket_by_name(request.id.bucket()); bucket != nullptr) { return bucket->execute(std::move(request), std::forward(handler)); } if (request.id.bucket().empty()) { return handler( request.make_response(make_key_value_error_context(errc::common::bucket_not_found, request.id), response_type{})); } auto bucket_name = request.id.bucket(); return open_bucket( bucket_name, [self = shared_from_this(), request = std::move(request), handler = std::forward(handler)](std::error_code ec) mutable { if (ec) { return handler(request.make_response(make_key_value_error_context(ec, request.id), response_type{})); } return self->execute(std::move(request), std::forward(handler)); }); } template, int> = 0> void execute(Request request, Handler&& handler) { using response_type = typename Request::encoded_response_type; if (stopped_) { return handler(request.make_response({ errc::network::cluster_closed }, response_type{})); } if (!is_feature_supported(request, session_manager_->configuration_capabilities())) { return handler(request.make_response({ errc::common::feature_not_available }, response_type{})); } if constexpr (operations::is_compound_operation_v) { return request.execute(shared_from_this(), std::forward(handler)); } else { return session_manager_->execute(std::move(request), std::forward(handler), origin_.credentials()); } } template, int> = 0> void execute_with_bucket_capability_check(Request request, bucket_capability cap, Handler&& handler) { auto bucket_name = request.bucket_name; return open_bucket( bucket_name, [self = shared_from_this(), bucket_name, cap, request = std::move(request), handler = std::forward(handler)]( std::error_code ec) mutable { if (ec) { handler(request.make_response({ ec }, {})); return; } return self->with_bucket_configuration( bucket_name, [self = std::move(self), cap, request = std::move(request), handler = std::forward(handler)]( std::error_code ec, topology::configuration config) mutable { if (ec) { handler(request.make_response({ ec }, {})); return; } if (!config.capabilities.has_bucket_capability(cap)) { handler(request.make_response({ errc::common::feature_not_available }, {})); return; } return self->execute(std::move(request), std::forward(handler)); }); }); } std::shared_ptr find_bucket_by_name(const std::string& name) { std::scoped_lock lock(buckets_mutex_); auto bucket = buckets_.find(name); if (bucket == buckets_.end()) { return {}; } return bucket->second; } void for_each_bucket(utils::movable_function)> handler) { std::vector> buckets{}; { std::scoped_lock lock(buckets_mutex_); buckets.reserve(buckets_.size()); for (const auto& [name, bucket] : buckets_) { buckets.push_back(bucket); } } for (auto bucket : buckets) { handler(bucket); } } void do_open(utils::movable_function handler) { // Warn users if idle_http_connection_timeout is too close to server idle timeouts if (origin_.options().idle_http_connection_timeout > std::chrono::milliseconds(4'500)) { CB_LOG_INFO("[{}]: The SDK may produce trivial warnings due to the idle HTTP connection timeout being set above the idle" "timeout of various services", id_); } // Warn users if they attempt to use Capella without TLS being enabled. bool has_capella_host = false; { bool has_non_capella_host = false; static std::string suffix = "cloud.couchbase.com"; for (const auto& node : origin_.get_hostnames()) { if (auto pos = node.find(suffix); pos != std::string::npos && pos + suffix.size() == node.size()) { has_capella_host = true; } else { has_non_capella_host = true; } } if (has_capella_host && !origin_.options().enable_tls) { CB_LOG_WARNING("[{}]: TLS is required when connecting to Couchbase Capella. Please enable TLS by prefixing " "the connection string with \"couchbases://\" (note the final 's').", id_); } if (origin_.options().enable_tls /* TLS is enabled */ && origin_.options().trust_certificate.empty() /* No CA certificate (or other SDK-specific trust source) is specified */ && origin_.options().trust_certificate_value.empty() /* and certificate value has not been specified */ && origin_.options().tls_verify != tls_verify_mode::none /* The user did not disable all TLS verification */ && has_non_capella_host /* The connection string has a hostname that does NOT end in ".cloud.couchbase.com" */) { CB_LOG_WARNING("[{}] When TLS is enabled, the cluster options must specify certificate(s) to trust or ensure that they are " "available in system CA store. (Unless connecting to cloud.couchbase.com.)", id_); } } if (origin_.options().enable_tls) { configure_tls_options(has_capella_host); if (origin_.options().trust_certificate.empty() && origin_.options().trust_certificate_value.empty()) { // trust certificate is not explicitly specified CB_LOG_DEBUG(R"([{}]: use default CA for TLS verify)", id_); std::error_code ec{}; // load system certificates tls_.set_default_verify_paths(ec); if (ec) { CB_LOG_WARNING(R"([{}]: failed to load system CAs: {})", id_, ec.message()); } // add the Capella Root CA in addition to system CAs tls_.add_certificate_authority( asio::const_buffer(couchbase::core::default_ca::capellaCaCert, strlen(couchbase::core::default_ca::capellaCaCert)), ec); if (ec) { CB_LOG_WARNING("[{}]: unable to load default CAs: {}", id_, ec.message()); // we don't consider this fatal and try to continue without it } if (const auto certificates = default_ca::mozilla_ca_certs(); !origin_.options().disable_mozilla_ca_certificates && !certificates.empty()) { CB_LOG_DEBUG("[{}]: loading {} CA certificates from Mozilla bundle. Update date: \"{}\", SHA256: \"{}\"", id_, certificates.size(), default_ca::mozilla_ca_certs_date(), default_ca::mozilla_ca_certs_sha256()); for (const auto& cert : certificates) { tls_.add_certificate_authority(asio::const_buffer(cert.body.data(), cert.body.size()), ec); if (ec) { CB_LOG_WARNING("[{}]: unable to load CA \"{}\" from Mozilla bundle: {}", id_, cert.authority, ec.message()); } } } } else { // trust certificate is explicitly specified std::error_code ec{}; // load only the explicit certificate // system and default capella certificates are not loaded if (!origin_.options().trust_certificate_value.empty()) { CB_LOG_DEBUG(R"([{}]: use TLS certificate passed through via options object)", id_); tls_.add_certificate_authority(asio::const_buffer(origin_.options().trust_certificate_value.data(), origin_.options().trust_certificate_value.size()), ec); if (ec) { CB_LOG_WARNING("[{}]: unable to load CA passed via options object: {}", id_, ec.message()); } } if (!origin_.options().trust_certificate.empty()) { CB_LOG_DEBUG(R"([{}]: use TLS verify file: "{}")", id_, origin_.options().trust_certificate); tls_.load_verify_file(origin_.options().trust_certificate, ec); if (ec) { CB_LOG_ERROR("[{}]: unable to load verify file \"{}\": {}", id_, origin_.options().trust_certificate, ec.message()); return close([ec, handler = std::move(handler)]() mutable { return handler(ec); }); } } } if (origin_.credentials().uses_certificate()) { std::error_code ec{}; CB_LOG_DEBUG(R"([{}]: use TLS certificate chain: "{}")", id_, origin_.certificate_path()); tls_.use_certificate_chain_file(origin_.certificate_path(), ec); if (ec) { CB_LOG_ERROR("[{}]: unable to load certificate chain \"{}\": {}", id_, origin_.certificate_path(), ec.message()); return close([ec, handler = std::move(handler)]() mutable { return handler(ec); }); } CB_LOG_DEBUG(R"([{}]: use TLS private key: "{}")", id_, origin_.key_path()); tls_.use_private_key_file(origin_.key_path(), asio::ssl::context::file_format::pem, ec); if (ec) { CB_LOG_ERROR("[{}]: unable to load private key \"{}\": {}", id_, origin_.key_path(), ec.message()); return close([ec, handler = std::move(handler)]() mutable { return handler(ec); }); } } session_ = io::mcbp_session(id_, ctx_, tls_, origin_, dns_srv_tracker_); } else { session_ = io::mcbp_session(id_, ctx_, origin_, dns_srv_tracker_); } session_->bootstrap( [self = shared_from_this(), handler = std::move(handler)](std::error_code ec, const topology::configuration& config) mutable { if (!ec) { if (self->origin_.options().network == "auto") { self->origin_.options().network = config.select_network(self->session_->bootstrap_hostname()); if (self->origin_.options().network == "default") { CB_LOG_DEBUG(R"({} detected network is "{}")", self->session_->log_prefix(), self->origin_.options().network); } else { CB_LOG_INFO(R"({} detected network is "{}")", self->session_->log_prefix(), self->origin_.options().network); } } if (self->origin_.options().network != "default") { self->origin_.set_nodes_from_config(config); CB_LOG_INFO("replace list of bootstrap nodes with addresses of alternative network \"{}\": [{}]", self->origin_.options().network, utils::join_strings(self->origin_.get_nodes(), ",")); } self->session_manager_->set_configuration(config, self->origin_.options()); self->session_->on_configuration_update(self->session_manager_); self->session_->on_stop([self]() { if (self->session_) { self->session_.reset(); } }); } if (ec) { return self->close([ec, handler = std::move(handler)]() mutable { handler(ec); }); } handler(ec); }); } void with_bucket_configuration(const std::string& bucket_name, utils::movable_function&& handler) { if (stopped_) { return handler(errc::network::cluster_closed, {}); } if (auto bucket = find_bucket_by_name(bucket_name); bucket != nullptr) { return bucket->with_configuration(std::move(handler)); } return handler(errc::common::bucket_not_found, {}); } void ping(std::optional report_id, std::optional bucket_name, std::set services, std::optional timeout, utils::movable_function handler) { if (!report_id) { report_id = std::make_optional(uuid::to_string(uuid::random())); } if (stopped_) { return handler({ report_id.value(), meta::sdk_id() }); } if (services.empty()) { services = { service_type::key_value, service_type::view, service_type::query, service_type::search, service_type::analytics, service_type::management, service_type::eventing, }; } asio::post(asio::bind_executor( ctx_, [cluster = shared_from_this(), report_id, bucket_name, services, timeout, handler = std::move(handler)]() mutable { auto collector = std::make_shared(report_id.value(), std::move(handler)); if (bucket_name) { if (services.find(service_type::key_value) != services.end()) { if (auto bucket = cluster->find_bucket_by_name(bucket_name.value()); bucket) { return bucket->ping(collector, timeout); } cluster->open_bucket(bucket_name.value(), [collector, cluster, bucket_name, timeout](std::error_code ec) { if (!ec) { if (auto bucket = cluster->find_bucket_by_name(bucket_name.value()); bucket) { return bucket->ping(collector, timeout); } } }); } } else { if (services.find(service_type::key_value) != services.end()) { if (cluster->session_) { cluster->session_->ping(collector->build_reporter(), timeout); } cluster->for_each_bucket([&collector, &timeout](auto bucket) { bucket->ping(collector, timeout); }); } cluster->session_manager_->ping(services, timeout, collector, cluster->origin_.credentials()); } })); } void diagnostics(std::optional report_id, utils::movable_function&& handler) { if (!report_id) { report_id = std::make_optional(uuid::to_string(uuid::random())); } if (stopped_) { return handler({ report_id.value(), couchbase::core::meta::sdk_id() }); } asio::post(asio::bind_executor(ctx_, [self = shared_from_this(), report_id, handler = std::move(handler)]() mutable { diag::diagnostics_result res{ report_id.value(), couchbase::core::meta::sdk_id() }; if (self->session_) { res.services[service_type::key_value].emplace_back(self->session_->diag_info()); } self->for_each_bucket([&res](const auto& bucket) { bucket->export_diag_info(res); }); self->session_manager_->export_diag_info(res); handler(std::move(res)); })); } void close(utils::movable_function&& handler) { if (stopped_) { return handler(); } stopped_ = true; asio::post(asio::bind_executor(ctx_, [self = shared_from_this(), handler = std::move(handler)]() mutable { if (self->session_) { self->session_->stop(retry_reason::do_not_retry); self->session_.reset(); } self->for_each_bucket([](auto bucket) { bucket->close(); }); self->session_manager_->close(); handler(); self->work_.reset(); if (self->tracer_) { self->tracer_->stop(); } self->tracer_.reset(); if (self->meter_) { self->meter_->stop(); } self->meter_.reset(); })); } auto direct_dispatch(const std::string& bucket_name, std::shared_ptr req) -> std::error_code { if (stopped_) { return errc::network::cluster_closed; } if (bucket_name.empty()) { return errc::common::invalid_argument; } if (auto bucket = find_bucket_by_name(bucket_name); bucket != nullptr) { return bucket->direct_dispatch(std::move(req)); } open_bucket(bucket_name, [self = shared_from_this(), req = std::move(req), bucket_name](std::error_code ec) mutable { if (ec) { return req->cancel(ec); } self->direct_dispatch(bucket_name, std::move(req)); }); return {}; } auto direct_re_queue(const std::string& bucket_name, std::shared_ptr req, bool is_retry) -> std::error_code { if (stopped_) { return errc::network::cluster_closed; } if (bucket_name.empty()) { return errc::common::invalid_argument; } if (auto bucket = find_bucket_by_name(bucket_name); bucket != nullptr) { return bucket->direct_re_queue(std::move(req), is_retry); } open_bucket(bucket_name, [self = shared_from_this(), bucket_name, req = std::move(req), is_retry](std::error_code ec) mutable { if (ec) { return req->cancel(ec); } self->direct_re_queue(bucket_name, std::move(req), is_retry); }); return {}; } private: std::string id_{ uuid::to_string(uuid::random()) }; asio::io_context& ctx_; asio::executor_work_guard work_; asio::ssl::context tls_{ asio::ssl::context::tls_client }; std::shared_ptr session_manager_; std::optional session_{}; std::shared_ptr dns_srv_tracker_{}; std::mutex buckets_mutex_{}; std::map> buckets_{}; couchbase::core::origin origin_{}; std::shared_ptr tracer_{ nullptr }; std::shared_ptr meter_{ nullptr }; std::atomic_bool stopped_{ false }; }; cluster::cluster(asio::io_context& ctx) : impl_{ std::make_shared(ctx) } { } auto cluster::direct_dispatch(const std::string& bucket_name, std::shared_ptr req) const -> std::error_code { if (impl_) { return impl_->direct_dispatch(bucket_name, std::move(req)); } return errc::network::cluster_closed; } auto cluster::direct_re_queue(const std::string& bucket_name, std::shared_ptr req, bool is_retry) const -> std::error_code { if (impl_) { return impl_->direct_re_queue(bucket_name, std::move(req), is_retry); } return errc::network::cluster_closed; } void cluster::close(utils::movable_function&& handler) const { if (impl_) { impl_->close(std::move(handler)); } } void cluster::open_bucket(const std::string& bucket_name, utils::movable_function&& handler) const { if (impl_) { impl_->open_bucket(bucket_name, std::move(handler)); } } void cluster::open(couchbase::core::origin origin, utils::movable_function&& handler) const { if (impl_) { impl_->open(std::move(origin), std::move(handler)); } } void cluster::diagnostics(std::optional report_id, utils::movable_function&& handler) const { if (impl_) { impl_->diagnostics(std::move(report_id), std::move(handler)); } } void cluster::ping(std::optional report_id, std::optional bucket_name, std::set services, std::optional timeout, utils::movable_function&& handler) const { if (impl_) { impl_->ping(std::move(report_id), std::move(bucket_name), std::move(services), std::move(timeout), std::move(handler)); } } void cluster::with_bucket_configuration(const std::string& bucket_name, utils::movable_function&& handler) const { if (impl_) { impl_->with_bucket_configuration(bucket_name, std::move(handler)); } } void cluster::close_bucket(const std::string& bucket_name, utils::movable_function&& handler) const { if (impl_) { impl_->close_bucket(bucket_name, std::move(handler)); } } std::pair cluster::origin() const { if (impl_) { return impl_->origin(); } return { errc::network::cluster_closed, {} }; } auto cluster::io_context() const -> asio::io_context& { return impl_->io_context(); } void cluster::execute(operations::append_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::decrement_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::exists_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::get_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::get_all_replicas_request request, utils::movable_function&& handler) const { return request.execute(impl_, std::move(handler)); } void cluster::execute(operations::get_and_lock_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::get_and_touch_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::get_any_replica_request request, utils::movable_function&& handler) const { return request.execute(impl_, std::move(handler)); } void cluster::execute(operations::get_projected_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::increment_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::insert_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::lookup_in_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::lookup_in_any_replica_request request, utils::movable_function&& handler) const { return request.execute(impl_, std::move(handler)); } void cluster::execute(operations::lookup_in_all_replicas_request request, utils::movable_function&& handler) const { return request.execute(impl_, std::move(handler)); } void cluster::execute(operations::mutate_in_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::prepend_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::query_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::remove_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::replace_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::search_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::touch_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::unlock_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::upsert_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::document_view_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::http_noop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_dataset_create_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_dataset_drop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_dataset_get_all_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_dataverse_create_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_dataverse_drop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_get_pending_mutations_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_index_create_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_index_drop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_index_get_all_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_link_connect_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_link_disconnect_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_link_drop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_link_get_all_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::bucket_create_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::bucket_drop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::bucket_flush_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::bucket_get_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::bucket_get_all_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::bucket_update_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::collection_create_request request, utils::movable_function&& handler) const { if (request.history.has_value()) { return impl_->execute_with_bucket_capability_check(std::move(request), bucket_capability::non_deduped_history, std::move(handler)); } return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::collection_update_request request, utils::movable_function&& handler) const { if (request.history.has_value()) { return impl_->execute_with_bucket_capability_check(std::move(request), bucket_capability::non_deduped_history, std::move(handler)); } return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::collection_drop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::collections_manifest_get_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::scope_create_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::scope_drop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::scope_get_all_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::eventing_deploy_function_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::eventing_drop_function_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::eventing_get_all_functions_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::eventing_get_function_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::eventing_get_status_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::eventing_pause_function_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::eventing_resume_function_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::eventing_undeploy_function_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::eventing_upsert_function_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::view_index_drop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::view_index_get_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::view_index_get_all_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::view_index_upsert_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::change_password_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::group_drop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::group_get_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::group_get_all_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::group_upsert_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::role_get_all_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::user_drop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::user_get_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::user_get_all_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::user_upsert_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::search_get_stats_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::search_index_analyze_document_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::search_index_control_ingest_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::search_index_control_plan_freeze_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::search_index_control_query_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::search_index_drop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::search_index_get_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::search_index_get_all_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::search_index_get_documents_count_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::search_index_get_stats_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::search_index_upsert_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::query_index_build_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::query_index_build_deferred_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::query_index_create_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::query_index_drop_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::query_index_get_all_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::query_index_get_all_deferred_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::bucket_describe_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::cluster_describe_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::cluster_developer_preview_enable_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::freeform_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(impl::get_replica_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(impl::observe_seqno_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_link_replace_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_link_replace_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_link_replace_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_link_create_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_link_create_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::management::analytics_link_create_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::analytics_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } void cluster::execute(operations::upsert_request_with_legacy_durability request, utils::movable_function&& handler) const { return request.execute(*this, std::move(handler)); } void cluster::execute(operations::insert_request_with_legacy_durability request, utils::movable_function&& handler) const { return request.execute(*this, std::move(handler)); } void cluster::execute(operations::append_request_with_legacy_durability request, utils::movable_function&& handler) const { return request.execute(*this, std::move(handler)); } void cluster::execute(operations::prepend_request_with_legacy_durability request, utils::movable_function&& handler) const { return request.execute(*this, std::move(handler)); } void cluster::execute(operations::replace_request_with_legacy_durability request, utils::movable_function&& handler) const { return request.execute(*this, std::move(handler)); } void cluster::execute(operations::mutate_in_request_with_legacy_durability request, utils::movable_function&& handler) const { return request.execute(*this, std::move(handler)); } void cluster::execute(operations::remove_request_with_legacy_durability request, utils::movable_function&& handler) const { return request.execute(*this, std::move(handler)); } void cluster::execute(operations::increment_request_with_legacy_durability request, utils::movable_function&& handler) const { return request.execute(*this, std::move(handler)); } void cluster::execute(operations::decrement_request_with_legacy_durability request, utils::movable_function&& handler) const { return request.execute(*this, std::move(handler)); } void cluster::execute(impl::lookup_in_replica_request request, utils::movable_function&& handler) const { return impl_->execute(std::move(request), std::move(handler)); } auto cluster::to_string() const -> std::string { return fmt::format( R"(#)", static_cast(this), impl_ ? static_cast(impl_.get()) : "(none)"); } } // namespace couchbase::core