couchbase 3.8.2 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/ext/CMakeLists.txt +16 -1
- data/ext/cache/cpm/{CPM_0.42.0.cmake → CPM_0.43.1.cmake} +18 -2
- data/ext/cache/extconf_include.rb +6 -6
- data/ext/cache/mozilla-ca-bundle.crt +121 -55
- data/ext/cache/mozilla-ca-bundle.sha256 +1 -1
- data/ext/couchbase/CMakeLists.txt +130 -21
- data/ext/couchbase/cmake/APKBUILD.in +25 -4
- data/ext/couchbase/cmake/Bundler.cmake +143 -34
- data/ext/couchbase/cmake/CPM.cmake +126 -12
- data/ext/couchbase/cmake/Cache.cmake +7 -1
- data/ext/couchbase/cmake/CompilerOptions.cmake +1 -1
- data/ext/couchbase/cmake/CompilerWarnings.cmake +17 -6
- data/ext/couchbase/cmake/GrpcProtobuf.cmake +481 -0
- data/ext/couchbase/cmake/OpenSSL.cmake +10 -3
- data/ext/couchbase/cmake/Packaging.cmake +519 -65
- data/ext/couchbase/cmake/Protostellar.cmake +250 -0
- data/ext/couchbase/cmake/Sanitizers.cmake +61 -50
- data/ext/couchbase/cmake/StaticAnalyzers.cmake +16 -5
- data/ext/couchbase/cmake/TestFramework.cmake +283 -0
- data/ext/couchbase/cmake/TestFrameworkAddTests.cmake +51 -0
- data/ext/couchbase/cmake/Testing.cmake +27 -9
- data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +141 -8
- data/ext/couchbase/cmake/VersionInfo.cmake +50 -0
- data/ext/couchbase/cmake/build_config.hxx.in +2 -1
- data/ext/couchbase/cmake/couchbase-cxx-client.spec.in +101 -23
- data/ext/couchbase/cmake/couchbase_cxx_client_static-config.cmake.in +7 -0
- data/ext/couchbase/cmake/couchbase_cxx_client_static.pc.in +2 -0
- data/ext/couchbase/cmake/gRPC_Generate.cmake +58 -0
- data/ext/couchbase/cmake/protobuf_Generate.cmake +69 -0
- data/ext/couchbase/cmake/require-git-checkout.cmake +25 -0
- data/ext/couchbase/cmake/tarball_glob.txt +49 -0
- data/ext/couchbase/core/agent_group.cxx +0 -8
- data/ext/couchbase/core/agent_group.hxx +0 -4
- data/ext/couchbase/core/analytics_stream.cxx +314 -0
- data/ext/couchbase/core/analytics_stream.hxx +129 -0
- data/ext/couchbase/core/analytics_stream_component.cxx +245 -0
- data/ext/couchbase/core/analytics_stream_component.hxx +67 -0
- data/ext/couchbase/core/app_telemetry_meter.cxx +25 -3
- data/ext/couchbase/core/app_telemetry_meter.hxx +77 -0
- data/ext/couchbase/core/app_telemetry_reporter.cxx +7 -6
- data/ext/couchbase/core/bucket.cxx +381 -87
- data/ext/couchbase/core/bucket.hxx +105 -25
- data/ext/couchbase/core/bucket_unit_test_api.hxx +57 -0
- data/ext/couchbase/core/capella.hxx +91 -0
- data/ext/couchbase/core/cluster.cxx +1554 -398
- data/ext/couchbase/core/cluster.hxx +39 -8
- data/ext/couchbase/core/cluster_agent.cxx +0 -8
- data/ext/couchbase/core/cluster_agent.hxx +0 -4
- data/ext/couchbase/core/cluster_options.hxx +26 -3
- data/ext/couchbase/core/collections_component.cxx +39 -15
- data/ext/couchbase/core/config_profiles.cxx +1 -1
- data/ext/couchbase/core/crud_component.cxx +2 -2
- data/ext/couchbase/core/crud_options.hxx +34 -0
- data/ext/couchbase/core/crypto/cbcrypto.cc +18 -13
- data/ext/couchbase/core/document_id_redaction.hxx +81 -0
- data/ext/couchbase/core/error_context/base_error_context.hxx +7 -6
- data/ext/couchbase/core/error_context/key_value.cxx +25 -2
- data/ext/couchbase/core/error_context/key_value.hxx +60 -33
- data/ext/couchbase/core/error_context/key_value_error_context.hxx +16 -15
- data/ext/couchbase/core/error_context/search.hxx +5 -0
- data/ext/couchbase/core/error_context/search_json.hxx +3 -0
- data/ext/couchbase/core/error_context/subdocument_error_context.hxx +19 -18
- data/ext/couchbase/core/error_context/view.hxx +5 -0
- data/ext/couchbase/core/free_form_http_request.cxx +237 -4
- data/ext/couchbase/core/free_form_http_request.hxx +41 -8
- data/ext/couchbase/core/http_component.cxx +300 -284
- data/ext/couchbase/core/http_component.hxx +0 -4
- data/ext/couchbase/core/impl/analytics.cxx +38 -5
- data/ext/couchbase/core/impl/analytics.hxx +34 -0
- data/ext/couchbase/core/impl/bootstrap_error.hxx +0 -3
- data/ext/couchbase/core/impl/collection.cxx +215 -202
- data/ext/couchbase/core/impl/collection_manager.cxx +1 -1
- data/ext/couchbase/core/impl/dns_srv_tracker.cxx +45 -18
- data/ext/couchbase/core/impl/error.cxx +22 -2
- data/ext/couchbase/core/impl/internal_analytics_stream_result.hxx +111 -0
- data/ext/couchbase/core/impl/internal_query_stream_result.hxx +110 -0
- data/ext/couchbase/core/impl/invoke_with_node_id.hxx +15 -7
- data/ext/couchbase/core/impl/key_value_error_category.cxx +6 -0
- data/ext/couchbase/core/impl/lookup_in_replica.cxx +10 -10
- data/ext/couchbase/core/impl/lookup_in_replica.hxx +4 -4
- data/ext/couchbase/core/impl/node_id.cxx +49 -13
- data/ext/couchbase/core/impl/observability_recorder.cxx +12 -2
- data/ext/couchbase/core/impl/observe_poll.cxx +63 -23
- data/ext/couchbase/core/impl/public_analytics_stream_result.cxx +372 -0
- data/ext/couchbase/core/impl/public_bucket.cxx +48 -2
- data/ext/couchbase/core/impl/public_cluster.cxx +229 -42
- data/ext/couchbase/core/impl/public_query_stream_result.cxx +380 -0
- data/ext/couchbase/core/impl/public_transaction_get_multi_replicas_from_preferred_server_group_result.cxx +42 -0
- data/ext/couchbase/core/impl/public_transaction_get_multi_result.cxx +42 -0
- data/ext/couchbase/core/impl/query.cxx +76 -7
- data/ext/couchbase/core/impl/query.hxx +36 -0
- data/ext/couchbase/core/impl/query_index_manager.cxx +54 -1
- data/ext/couchbase/core/impl/replica_utils.cxx +101 -0
- data/ext/couchbase/core/impl/replica_utils.hxx +64 -0
- data/ext/couchbase/core/impl/resolve_node_id.hxx +125 -0
- data/ext/couchbase/core/impl/scope.cxx +70 -0
- data/ext/couchbase/core/impl/search.cxx +31 -1
- data/ext/couchbase/core/impl/transactions.cxx +130 -0
- data/ext/couchbase/core/impl/wait_until_ready.cxx +439 -0
- data/ext/couchbase/core/impl/wait_until_ready.hxx +110 -0
- data/ext/couchbase/core/impl/with_bucket_config_or_timeout.hxx +180 -0
- data/ext/couchbase/core/impl/with_cancellation.hxx +11 -3
- data/ext/couchbase/core/io/configuration_belongs_to_session.hxx +1 -2
- data/ext/couchbase/core/io/dns_client.cxx +122 -45
- data/ext/couchbase/core/io/dns_codec.hxx +21 -21
- data/ext/couchbase/core/io/dns_config.cxx +21 -6
- data/ext/couchbase/core/io/http_command.hxx +149 -121
- data/ext/couchbase/core/io/http_error.hxx +107 -0
- data/ext/couchbase/core/io/http_parser.cxx +4 -0
- data/ext/couchbase/core/io/http_session.cxx +606 -154
- data/ext/couchbase/core/io/http_session.hxx +147 -30
- data/ext/couchbase/core/io/http_session_manager.hxx +329 -382
- data/ext/couchbase/core/io/http_streaming_parser.cxx +4 -0
- data/ext/couchbase/core/io/http_streaming_response.cxx +328 -43
- data/ext/couchbase/core/io/http_streaming_response.hxx +37 -3
- data/ext/couchbase/core/io/mcbp_buffer_pool.hxx +122 -0
- data/ext/couchbase/core/io/mcbp_command.hxx +150 -51
- data/ext/couchbase/core/io/mcbp_command_id.hxx +45 -0
- data/ext/couchbase/core/io/mcbp_message.cxx +9 -1
- data/ext/couchbase/core/io/mcbp_message.hxx +19 -0
- data/ext/couchbase/core/io/mcbp_output_queue.hxx +150 -0
- data/ext/couchbase/core/io/mcbp_parser.cxx +24 -7
- data/ext/couchbase/core/io/mcbp_session.cxx +309 -301
- data/ext/couchbase/core/io/mcbp_session.hxx +27 -18
- data/ext/couchbase/core/io/opaque_ring_table.hxx +145 -0
- data/ext/couchbase/core/io/retry_orchestrator.hxx +6 -5
- data/ext/couchbase/core/io/stream_deadline.hxx +50 -0
- data/ext/couchbase/core/io/streams.cxx +151 -21
- data/ext/couchbase/core/io/streams.hxx +48 -1
- data/ext/couchbase/core/logger/logger.cxx +36 -0
- data/ext/couchbase/core/logger/logger.hxx +46 -6
- data/ext/couchbase/core/logger/redaction.hxx +503 -0
- data/ext/couchbase/core/management/eventing_status.hxx +10 -0
- data/ext/couchbase/core/management/eventing_status_json.hxx +4 -0
- data/ext/couchbase/core/mcbp/big_endian.cxx +1 -1
- data/ext/couchbase/core/mcbp/codec.cxx +27 -18
- data/ext/couchbase/core/mcbp/operation_queue.cxx +2 -2
- data/ext/couchbase/core/mcbp/packet.hxx +2 -2
- data/ext/couchbase/core/mcbp/queue_request.cxx +23 -6
- data/ext/couchbase/core/mcbp/queue_request.hxx +21 -0
- data/ext/couchbase/core/meta/features.hxx +13 -0
- data/ext/couchbase/core/meta/version.cxx +6 -3
- data/ext/couchbase/core/metrics/logging_meter.cxx +5 -10
- data/ext/couchbase/core/metrics/meter_wrapper.cxx +63 -33
- data/ext/couchbase/core/metrics/meter_wrapper.hxx +55 -3
- data/ext/couchbase/core/operations/analytics_response_parsing.cxx +177 -0
- data/ext/couchbase/core/operations/analytics_response_parsing.hxx +55 -0
- data/ext/couchbase/core/operations/document_analytics.cxx +48 -130
- data/ext/couchbase/core/operations/document_analytics.hxx +14 -2
- data/ext/couchbase/core/operations/document_get_all_replicas.hxx +15 -7
- data/ext/couchbase/core/operations/document_get_any_replica.hxx +19 -8
- data/ext/couchbase/core/operations/document_get_projected.cxx +1 -1
- data/ext/couchbase/core/{impl/get_replica.cxx → operations/document_get_replica.cxx} +21 -3
- data/ext/couchbase/core/{impl/get_replica.hxx → operations/document_get_replica.hxx} +31 -4
- data/ext/couchbase/core/operations/document_lookup_in.cxx +10 -10
- data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +228 -198
- data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +215 -217
- data/ext/couchbase/core/operations/document_query.cxx +108 -221
- data/ext/couchbase/core/operations/document_query.hxx +18 -4
- data/ext/couchbase/core/operations/document_search.cxx +52 -8
- data/ext/couchbase/core/operations/document_search.hxx +3 -0
- data/ext/couchbase/core/operations/document_view.cxx +3 -3
- data/ext/couchbase/core/operations/management/analytics_dataset_create.cxx +15 -8
- data/ext/couchbase/core/operations/management/analytics_dataset_create.hxx +4 -0
- data/ext/couchbase/core/operations/management/analytics_dataset_drop.cxx +11 -6
- data/ext/couchbase/core/operations/management/analytics_dataset_get_all.cxx +3 -2
- data/ext/couchbase/core/operations/management/analytics_dataverse_create.cxx +10 -5
- data/ext/couchbase/core/operations/management/analytics_dataverse_drop.cxx +10 -5
- data/ext/couchbase/core/operations/management/analytics_get_pending_mutations.cxx +2 -1
- data/ext/couchbase/core/operations/management/analytics_index_create.cxx +33 -8
- data/ext/couchbase/core/operations/management/analytics_index_drop.cxx +12 -7
- data/ext/couchbase/core/operations/management/analytics_index_get_all.cxx +3 -2
- data/ext/couchbase/core/operations/management/analytics_link_connect.cxx +11 -6
- data/ext/couchbase/core/operations/management/analytics_link_create.cxx +3 -3
- data/ext/couchbase/core/operations/management/analytics_link_disconnect.cxx +10 -4
- data/ext/couchbase/core/operations/management/analytics_link_drop.cxx +6 -4
- data/ext/couchbase/core/operations/management/analytics_link_get_all.cxx +4 -3
- data/ext/couchbase/core/operations/management/analytics_link_replace.cxx +3 -3
- data/ext/couchbase/core/operations/management/analytics_link_utils.hxx +1 -1
- data/ext/couchbase/core/operations/management/change_password.cxx +2 -0
- data/ext/couchbase/core/operations/management/collection_drop.cxx +5 -1
- data/ext/couchbase/core/operations/management/error_utils.cxx +59 -5
- data/ext/couchbase/core/operations/management/error_utils.hxx +29 -1
- data/ext/couchbase/core/operations/management/eventing_deploy_function.cxx +9 -3
- data/ext/couchbase/core/operations/management/eventing_drop_function.cxx +8 -3
- data/ext/couchbase/core/operations/management/eventing_get_all_functions.cxx +4 -2
- data/ext/couchbase/core/operations/management/eventing_get_function.cxx +5 -3
- data/ext/couchbase/core/operations/management/eventing_get_status.cxx +4 -2
- data/ext/couchbase/core/operations/management/eventing_pause_function.cxx +9 -3
- data/ext/couchbase/core/operations/management/eventing_resume_function.cxx +9 -3
- data/ext/couchbase/core/operations/management/eventing_undeploy_function.cxx +9 -3
- data/ext/couchbase/core/operations/management/eventing_upsert_function.cxx +9 -3
- data/ext/couchbase/core/operations/management/group_drop.cxx +3 -1
- data/ext/couchbase/core/operations/management/group_get.cxx +3 -1
- data/ext/couchbase/core/operations/management/group_upsert.cxx +11 -8
- data/ext/couchbase/core/operations/management/query_index_build.cxx +5 -2
- data/ext/couchbase/core/operations/management/query_index_create.cxx +9 -3
- data/ext/couchbase/core/operations/management/query_index_drop.cxx +11 -4
- data/ext/couchbase/core/operations/management/query_index_get_all.cxx +1 -1
- data/ext/couchbase/core/operations/management/query_index_get_all_deferred.cxx +1 -1
- data/ext/couchbase/core/operations/management/scope_drop.cxx +5 -1
- data/ext/couchbase/core/operations/management/scope_get_all.cxx +6 -1
- data/ext/couchbase/core/operations/management/search_get_stats.cxx +10 -0
- data/ext/couchbase/core/operations/management/search_index_analyze_document.cxx +3 -2
- data/ext/couchbase/core/operations/management/search_index_control_ingest.cxx +4 -3
- data/ext/couchbase/core/operations/management/search_index_control_plan_freeze.cxx +4 -3
- data/ext/couchbase/core/operations/management/search_index_control_query.cxx +4 -3
- data/ext/couchbase/core/operations/management/search_index_drop.cxx +2 -2
- data/ext/couchbase/core/operations/management/search_index_get.cxx +2 -2
- data/ext/couchbase/core/operations/management/search_index_get_documents_count.cxx +3 -2
- data/ext/couchbase/core/operations/management/search_index_get_stats.cxx +3 -1
- data/ext/couchbase/core/operations/management/search_index_upsert.cxx +2 -2
- data/ext/couchbase/core/operations/management/user_drop.cxx +3 -1
- data/ext/couchbase/core/operations/management/user_get.cxx +3 -1
- data/ext/couchbase/core/operations/management/user_upsert.cxx +12 -9
- data/ext/couchbase/core/operations/management/view_index_drop.cxx +1 -1
- data/ext/couchbase/core/operations/management/view_index_get.cxx +1 -1
- data/ext/couchbase/core/operations/management/view_index_upsert.cxx +1 -1
- data/ext/couchbase/core/operations/operation_traits.hxx +18 -0
- data/ext/couchbase/core/operations/query_response_parsing.cxx +211 -0
- data/ext/couchbase/core/operations/query_response_parsing.hxx +60 -0
- data/ext/couchbase/core/operations_fwd.hxx +3 -3
- data/ext/couchbase/core/origin.cxx +54 -26
- data/ext/couchbase/core/origin.hxx +12 -0
- data/ext/couchbase/core/orphan_reporter.cxx +2 -1
- data/ext/couchbase/core/platform/random.cc +7 -7
- data/ext/couchbase/core/protocol/client_request.cxx +2 -2
- data/ext/couchbase/core/protocol/client_request.hxx +38 -21
- data/ext/couchbase/core/protocol/client_response.cxx +11 -3
- data/ext/couchbase/core/protocol/client_response.hxx +27 -6
- data/ext/couchbase/core/protocol/cmd_append.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_cluster_map_change_notification.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_decrement.cxx +6 -6
- data/ext/couchbase/core/protocol/cmd_get.cxx +1 -1
- data/ext/couchbase/core/protocol/cmd_get_and_lock.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_get_and_touch.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_get_cluster_config.cxx +6 -4
- data/ext/couchbase/core/protocol/cmd_get_collection_id.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_get_error_map.cxx +1 -1
- data/ext/couchbase/core/protocol/cmd_get_meta.cxx +4 -4
- data/ext/couchbase/core/protocol/cmd_get_replica.cxx +1 -1
- data/ext/couchbase/core/protocol/cmd_hello.cxx +1 -1
- data/ext/couchbase/core/protocol/cmd_increment.cxx +6 -6
- data/ext/couchbase/core/protocol/cmd_insert.cxx +4 -4
- data/ext/couchbase/core/protocol/cmd_lookup_in.cxx +4 -3
- data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +4 -3
- data/ext/couchbase/core/protocol/cmd_mutate_in.cxx +10 -9
- data/ext/couchbase/core/protocol/cmd_mutate_in.hxx +10 -10
- data/ext/couchbase/core/protocol/cmd_observe_seqno.cxx +7 -7
- data/ext/couchbase/core/protocol/cmd_prepend.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_remove.cxx +2 -2
- data/ext/couchbase/core/protocol/cmd_replace.cxx +4 -4
- data/ext/couchbase/core/protocol/cmd_touch.cxx +1 -1
- data/ext/couchbase/core/protocol/cmd_upsert.cxx +4 -4
- data/ext/couchbase/core/protocol/datatype.hxx +118 -1
- data/ext/couchbase/core/protocol/frame_info_utils.cxx +1 -1
- data/ext/couchbase/core/protocol/magic.hxx +4 -0
- data/ext/couchbase/core/protocol/magic_fmt.hxx +3 -0
- data/ext/couchbase/core/protocol/server_request.hxx +3 -1
- data/ext/couchbase/core/protocol/status.cxx +1 -1
- data/ext/couchbase/core/protostellar/analytics_converter.hxx +171 -0
- data/ext/couchbase/core/protostellar/bucket_admin_converter.hxx +301 -0
- data/ext/couchbase/core/protostellar/collection_admin_converter.hxx +92 -0
- data/ext/couchbase/core/protostellar/component.cxx +2445 -0
- data/ext/couchbase/core/protostellar/component.hxx +391 -0
- data/ext/couchbase/core/{columnar/bootstrap_notification_subscriber.hxx → protostellar/compression_settings.hxx} +11 -10
- data/ext/couchbase/core/protostellar/credentials.cxx +233 -0
- data/ext/couchbase/core/protostellar/credentials.hxx +88 -0
- data/ext/couchbase/core/protostellar/dispatcher.cxx +57 -0
- data/ext/couchbase/core/protostellar/dispatcher.hxx +482 -0
- data/ext/couchbase/core/protostellar/error_utils.cxx +293 -0
- data/ext/couchbase/core/protostellar/error_utils.hxx +97 -0
- data/ext/couchbase/core/protostellar/json_payload.hxx +49 -0
- data/ext/couchbase/core/protostellar/kv_converter.cxx +70 -0
- data/ext/couchbase/core/protostellar/kv_converter.hxx +600 -0
- data/ext/couchbase/core/protostellar/query_converter.hxx +265 -0
- data/ext/couchbase/core/protostellar/query_index_admin_converter.hxx +291 -0
- data/ext/couchbase/core/protostellar/query_proto.hxx +73 -0
- data/ext/couchbase/core/protostellar/search_converter.hxx +244 -0
- data/ext/couchbase/core/protostellar/search_index_admin_converter.hxx +156 -0
- data/ext/couchbase/core/protostellar/view_converter.hxx +158 -0
- data/ext/couchbase/core/query_stream.cxx +447 -0
- data/ext/couchbase/core/query_stream.hxx +141 -0
- data/ext/couchbase/core/query_stream_component.cxx +243 -0
- data/ext/couchbase/core/query_stream_component.hxx +68 -0
- data/ext/couchbase/core/range_scan_load_balancer.cxx +15 -8
- data/ext/couchbase/core/range_scan_orchestrator.cxx +15 -3
- data/ext/couchbase/core/response_handler.hxx +41 -4
- data/ext/couchbase/core/row_streamer.cxx +332 -46
- data/ext/couchbase/core/row_streamer.hxx +31 -1
- data/ext/couchbase/core/row_streamer_options.hxx +55 -0
- data/ext/couchbase/core/sasl/client.h +3 -0
- data/ext/couchbase/core/search_scoring.hxx +66 -0
- data/ext/couchbase/core/stream_error_details.hxx +69 -0
- data/ext/couchbase/core/timeout_defaults.hxx +0 -1
- data/ext/couchbase/core/topology/capabilities.hxx +6 -0
- data/ext/couchbase/core/topology/capabilities_fmt.hxx +3 -0
- data/ext/couchbase/core/topology/configuration.cxx +21 -6
- data/ext/couchbase/core/topology/configuration.hxx +29 -8
- data/ext/couchbase/core/topology/configuration_json.hxx +3 -0
- data/ext/couchbase/core/topology/error_map_json.hxx +3 -1
- data/ext/couchbase/core/tracing/threshold_logging_tracer.cxx +70 -4
- data/ext/couchbase/core/tracing/threshold_logging_tracer.hxx +10 -0
- data/ext/couchbase/core/transactions/active_transaction_record.cxx +0 -34
- data/ext/couchbase/core/transactions/attempt_context_impl.cxx +108 -63
- data/ext/couchbase/core/transactions/attempt_context_impl.hxx +19 -2
- data/ext/couchbase/core/transactions/error_list.hxx +2 -2
- data/ext/couchbase/core/transactions/get_multi_fetch.hxx +160 -0
- data/ext/couchbase/core/transactions/get_multi_orchestrator.cxx +326 -125
- data/ext/couchbase/core/transactions/get_multi_transaction_id.hxx +62 -0
- data/ext/couchbase/core/transactions/internal/atr_entry.hxx +22 -2
- data/ext/couchbase/core/transactions/internal/transaction_context.hxx +2 -2
- data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +23 -1
- data/ext/couchbase/core/transactions/internal/utils.hxx +50 -14
- data/ext/couchbase/core/transactions/staged_mutation.cxx +23 -18
- data/ext/couchbase/core/transactions/staged_mutation.hxx +15 -4
- data/ext/couchbase/core/transactions/transaction_context.cxx +55 -40
- data/ext/couchbase/core/transactions/transaction_get_result.hxx +16 -0
- data/ext/couchbase/core/transactions/transactions.cxx +3 -55
- data/ext/couchbase/core/transactions/transactions_cleanup.cxx +165 -72
- data/ext/couchbase/core/transactions/transactions_config.cxx +0 -11
- data/ext/couchbase/core/transactions/utils.cxx +10 -0
- data/ext/couchbase/core/transactions/waitable_op_list.hxx +2 -2
- data/ext/couchbase/core/transactions.hxx +9 -9
- data/ext/couchbase/core/utils/byteswap.hxx +166 -9
- data/ext/couchbase/core/utils/connection_string.cxx +50 -85
- data/ext/couchbase/core/utils/connection_string.hxx +15 -0
- data/ext/couchbase/core/{columnar/backoff_calculator.hxx → utils/deadline.hxx} +13 -13
- data/ext/couchbase/core/utils/duration_parser.cxx +1 -1
- data/ext/couchbase/core/utils/json_streaming_lexer.cxx +48 -5
- data/ext/couchbase/core/utils/json_streaming_lexer.hxx +4 -1
- data/ext/couchbase/core/utils/movable_function.hxx +301 -52
- data/ext/couchbase/core/utils/name_codec.hxx +154 -11
- data/ext/couchbase/core/{columnar/retry_info.hxx → utils/process_id.hxx} +18 -13
- data/ext/couchbase/core/utils/url_codec.cxx +12 -51
- data/ext/couchbase/core/websocket_codec.cxx +3 -29
- data/ext/couchbase/couchbase/analytics_options.hxx +21 -10
- data/ext/couchbase/couchbase/analytics_row.hxx +105 -0
- data/ext/couchbase/couchbase/analytics_stream_result.hxx +266 -0
- data/ext/couchbase/couchbase/boolean_query.hxx +3 -3
- data/ext/couchbase/couchbase/bucket.hxx +57 -0
- data/ext/couchbase/couchbase/cluster.hxx +183 -5
- data/ext/couchbase/{core/columnar/security_options.hxx → couchbase/cluster_state.hxx} +18 -14
- data/ext/couchbase/couchbase/codec/lenient_json_transcoder.hxx +69 -0
- data/ext/couchbase/couchbase/collection.hxx +234 -14
- data/ext/couchbase/couchbase/common_durability_options.hxx +18 -3
- data/ext/couchbase/couchbase/conjunction_query.hxx +1 -1
- data/ext/couchbase/couchbase/create_dataset_analytics_options.hxx +6 -1
- data/ext/couchbase/couchbase/date_range_query.hxx +2 -2
- data/ext/couchbase/couchbase/decrement_options.hxx +1 -1
- data/ext/couchbase/couchbase/disjunction_query.hxx +1 -1
- data/ext/couchbase/couchbase/error.hxx +32 -0
- data/ext/couchbase/couchbase/error_codes.hxx +32 -0
- data/ext/couchbase/couchbase/geo_bounding_box_query.hxx +1 -1
- data/ext/couchbase/couchbase/geo_distance_query.hxx +1 -1
- data/ext/couchbase/couchbase/geo_polygon_query.hxx +1 -1
- data/ext/couchbase/couchbase/get_replica_options.hxx +68 -0
- data/ext/couchbase/couchbase/get_replica_result.hxx +5 -3
- data/ext/couchbase/couchbase/get_replica_strategy.hxx +161 -0
- data/ext/couchbase/couchbase/get_result.hxx +1 -0
- data/ext/couchbase/couchbase/increment_options.hxx +1 -1
- data/ext/couchbase/couchbase/insert_options.hxx +1 -1
- data/ext/couchbase/couchbase/match_phrase_query.hxx +1 -1
- data/ext/couchbase/couchbase/match_query.hxx +1 -1
- data/ext/couchbase/couchbase/mutate_in_options.hxx +3 -3
- data/ext/couchbase/couchbase/network_options.hxx +1 -1
- data/ext/couchbase/couchbase/node_id.hxx +42 -14
- data/ext/couchbase/couchbase/node_id_for_options.hxx +7 -1
- data/ext/couchbase/couchbase/node_ids_options.hxx +8 -2
- data/ext/couchbase/couchbase/numeric_range_query.hxx +1 -1
- data/ext/couchbase/couchbase/phrase_query.hxx +1 -1
- data/ext/couchbase/couchbase/prefix_query.hxx +1 -1
- data/ext/couchbase/couchbase/query_options.hxx +23 -12
- data/ext/couchbase/couchbase/query_row.hxx +111 -0
- data/ext/couchbase/couchbase/query_stream_result.hxx +302 -0
- data/ext/couchbase/couchbase/query_string_query.hxx +3 -3
- data/ext/couchbase/couchbase/regexp_query.hxx +1 -1
- data/ext/couchbase/couchbase/replace_options.hxx +3 -3
- data/ext/couchbase/couchbase/result.hxx +14 -3
- data/ext/couchbase/couchbase/scan_result_item.hxx +1 -0
- data/ext/couchbase/couchbase/scope.hxx +85 -2
- data/ext/couchbase/couchbase/search_options.hxx +58 -8
- data/ext/couchbase/couchbase/search_scoring.hxx +94 -0
- data/ext/couchbase/couchbase/search_scoring_none.hxx +51 -0
- data/ext/couchbase/couchbase/search_scoring_reciprocal_rank_fusion.hxx +88 -0
- data/ext/couchbase/couchbase/search_scoring_relative_score_fusion.hxx +70 -0
- data/ext/couchbase/couchbase/term_range_query.hxx +1 -1
- data/ext/couchbase/couchbase/tracing/request_span.hxx +46 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +23 -21
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_result.hxx +24 -19
- data/ext/couchbase/couchbase/transactions/transaction_get_result.hxx +17 -3
- data/ext/couchbase/couchbase/transactions/transactions_config.hxx +4 -2
- data/ext/couchbase/couchbase/transactions.hxx +27 -18
- data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
- data/ext/couchbase/couchbase/vector_query.hxx +1 -1
- data/ext/couchbase/couchbase/wait_until_ready_options.hxx +111 -0
- data/ext/couchbase/couchbase/wildcard_query.hxx +1 -1
- data/ext/extconf.rb +15 -10
- data/ext/rcb_analytics.cxx +82 -102
- data/ext/rcb_backend.cxx +42 -36
- data/ext/rcb_backend.hxx +2 -0
- data/ext/rcb_buckets.cxx +47 -59
- data/ext/rcb_collections.cxx +42 -52
- data/ext/rcb_crud.cxx +345 -224
- data/ext/rcb_diagnostics.cxx +117 -27
- data/ext/rcb_exceptions.cxx +152 -99
- data/ext/rcb_exceptions.hxx +25 -0
- data/ext/rcb_extras.cxx +18 -19
- data/ext/rcb_hdr_histogram.cxx +19 -4
- data/ext/rcb_logger.cxx +202 -27
- data/ext/rcb_logger.hxx +12 -0
- data/ext/rcb_multi.cxx +40 -40
- data/ext/rcb_observability.cxx +7 -5
- data/ext/rcb_query.cxx +99 -142
- data/ext/rcb_range_scan.cxx +41 -35
- data/ext/rcb_search.cxx +98 -99
- data/ext/rcb_users.cxx +36 -56
- data/ext/rcb_utils.cxx +100 -37
- data/ext/rcb_utils.hxx +287 -33
- data/ext/rcb_version.cxx +1 -1
- data/ext/rcb_views.cxx +55 -51
- data/lib/active_support/cache/couchbase_store.rb +77 -12
- data/lib/couchbase/bucket.rb +20 -0
- data/lib/couchbase/cluster.rb +17 -0
- data/lib/couchbase/collection.rb +36 -1
- data/lib/couchbase/errors.rb +18 -0
- data/lib/couchbase/fork_hooks.rb +16 -7
- data/lib/couchbase/get_replica_strategy.rb +89 -0
- data/lib/couchbase/logger.rb +1 -1
- data/lib/couchbase/management/analytics_index_manager.rb +15 -15
- data/lib/couchbase/management/bucket_manager.rb +6 -6
- data/lib/couchbase/management/collection_manager.rb +6 -6
- data/lib/couchbase/management/query_index_manager.rb +7 -7
- data/lib/couchbase/management/search_index_manager.rb +13 -13
- data/lib/couchbase/management/user_manager.rb +10 -10
- data/lib/couchbase/management/view_index_manager.rb +5 -5
- data/lib/couchbase/options.rb +174 -42
- data/lib/couchbase/protostellar/bucket.rb +4 -0
- data/lib/couchbase/protostellar/cluster.rb +4 -0
- data/lib/couchbase/protostellar/collection.rb +4 -0
- data/lib/couchbase/protostellar/error_handling.rb +2 -1
- data/lib/couchbase/protostellar/request.rb +1 -0
- data/lib/couchbase/protostellar/request_generator/kv.rb +16 -2
- data/lib/couchbase/protostellar/request_generator/search.rb +17 -1
- data/lib/couchbase/protostellar/retry/orchestrator.rb +3 -2
- data/lib/couchbase/protostellar/retry/strategies/fail_fast.rb +33 -0
- data/lib/couchbase/protostellar/retry/strategies.rb +1 -0
- data/lib/couchbase/search_options.rb +95 -6
- data/lib/couchbase/version.rb +1 -1
- metadata +91 -33
- data/ext/0001-fix-build-for-mingw-w64-ucrt-x86_64-toolchain.patch +0 -40
- data/ext/couchbase/core/columnar/agent.hxx +0 -71
- data/ext/couchbase/core/columnar/agent_config.hxx +0 -59
- data/ext/couchbase/core/columnar/background_bootstrap_listener.hxx +0 -42
- data/ext/couchbase/core/columnar/backoff_calculator.cxx +0 -62
- data/ext/couchbase/core/columnar/columnar_agent.cxx +0 -147
- data/ext/couchbase/core/columnar/columnar_agent_config.cxx +0 -48
- data/ext/couchbase/core/columnar/columnar_error.cxx +0 -51
- data/ext/couchbase/core/columnar/database_management_options.hxx +0 -56
- data/ext/couchbase/core/columnar/error.hxx +0 -51
- data/ext/couchbase/core/columnar/error_codes.cxx +0 -107
- data/ext/couchbase/core/columnar/error_codes.hxx +0 -75
- data/ext/couchbase/core/columnar/management_component.cxx +0 -300
- data/ext/couchbase/core/columnar/management_component.hxx +0 -56
- data/ext/couchbase/core/columnar/query_component.cxx +0 -523
- data/ext/couchbase/core/columnar/query_component.hxx +0 -57
- data/ext/couchbase/core/columnar/query_options.hxx +0 -59
- data/ext/couchbase/core/columnar/query_result.cxx +0 -130
- data/ext/couchbase/core/columnar/query_result.hxx +0 -78
- data/ext/couchbase/core/io/config_tracker.cxx +0 -687
- data/ext/couchbase/core/io/config_tracker.hxx +0 -123
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
2
|
+
/*
|
|
3
|
+
* Copyright 2026-Present Couchbase, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#include "wait_until_ready.hxx"
|
|
19
|
+
|
|
20
|
+
#include "core/diagnostics.hxx"
|
|
21
|
+
#include "core/logger/logger.hxx"
|
|
22
|
+
#include "core/logger/redaction.hxx"
|
|
23
|
+
#include "core/topology/configuration.hxx"
|
|
24
|
+
#include "with_bucket_config_or_timeout.hxx"
|
|
25
|
+
|
|
26
|
+
#include <couchbase/error.hxx>
|
|
27
|
+
#include <couchbase/error_codes.hxx>
|
|
28
|
+
|
|
29
|
+
#include <asio/post.hpp>
|
|
30
|
+
#include <asio/steady_timer.hpp>
|
|
31
|
+
|
|
32
|
+
#include <algorithm>
|
|
33
|
+
#include <atomic>
|
|
34
|
+
#include <cstdint>
|
|
35
|
+
#include <memory>
|
|
36
|
+
#include <string_view>
|
|
37
|
+
#include <utility>
|
|
38
|
+
|
|
39
|
+
namespace couchbase::core::impl
|
|
40
|
+
{
|
|
41
|
+
namespace
|
|
42
|
+
{
|
|
43
|
+
// The SDK refreshes the cluster map on its own interval (2.5s by default), so a freshly created
|
|
44
|
+
// bucket's vbucket map will not change faster than that regardless; a short poll just keeps latency
|
|
45
|
+
// low once it does.
|
|
46
|
+
constexpr std::chrono::milliseconds poll_interval{ 100 };
|
|
47
|
+
|
|
48
|
+
auto
|
|
49
|
+
data_node_count(const topology::configuration& config) -> std::uint32_t
|
|
50
|
+
{
|
|
51
|
+
return static_cast<std::uint32_t>(
|
|
52
|
+
std::count_if(config.nodes.begin(), config.nodes.end(), [](const auto& node) {
|
|
53
|
+
return node.services_plain.key_value.has_value() || node.services_tls.key_value.has_value();
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
} // namespace
|
|
57
|
+
|
|
58
|
+
// online : every requested service has at least one endpoint and ALL of them are ok.
|
|
59
|
+
// degraded : every requested service has at least one endpoint that is ok.
|
|
60
|
+
// When no services were requested we consider whatever the report contains; an empty report
|
|
61
|
+
// (nothing pinged back yet) is treated as not-ready.
|
|
62
|
+
auto
|
|
63
|
+
ping_predicate_satisfied(const diag::ping_result& report,
|
|
64
|
+
couchbase::cluster_state state,
|
|
65
|
+
const std::set<service_type>& requested) -> bool
|
|
66
|
+
{
|
|
67
|
+
const auto& endpoints = report.services;
|
|
68
|
+
|
|
69
|
+
std::set<service_type> services = requested;
|
|
70
|
+
if (services.empty()) {
|
|
71
|
+
for (const auto& [service, reports] : endpoints) {
|
|
72
|
+
services.insert(service);
|
|
73
|
+
}
|
|
74
|
+
if (services.empty()) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for (const auto service : services) {
|
|
80
|
+
const auto it = endpoints.find(service);
|
|
81
|
+
if (it == endpoints.end() || it->second.empty()) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const auto& reports = it->second;
|
|
85
|
+
const auto is_ok = [](const diag::endpoint_ping_info& r) {
|
|
86
|
+
return r.state == diag::ping_state::ok;
|
|
87
|
+
};
|
|
88
|
+
const bool ok = (state == couchbase::cluster_state::online)
|
|
89
|
+
? std::all_of(reports.begin(), reports.end(), is_ok)
|
|
90
|
+
: std::any_of(reports.begin(), reports.end(), is_ok);
|
|
91
|
+
if (!ok) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// One replica per data node besides the one holding the active, up to num_replicas. The server
|
|
99
|
+
// leaves a replica slot at -1 for as long as there is no node to place it on. A configuration
|
|
100
|
+
// listing no data node is not capped.
|
|
101
|
+
auto
|
|
102
|
+
placeable_replicas(const topology::configuration& config) -> std::uint32_t
|
|
103
|
+
{
|
|
104
|
+
const auto data_nodes = data_node_count(config);
|
|
105
|
+
if (data_nodes == 0) {
|
|
106
|
+
return config.num_replicas.value_or(0); // no node list to cap against
|
|
107
|
+
}
|
|
108
|
+
return std::min(config.num_replicas.value_or(0), data_nodes - 1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
auto
|
|
112
|
+
unplaceable_replicas(const topology::configuration& config) -> std::uint32_t
|
|
113
|
+
{
|
|
114
|
+
return config.num_replicas.value_or(0) - placeable_replicas(config);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
void
|
|
118
|
+
warn_if_replicas_unplaceable(const topology::configuration& config,
|
|
119
|
+
std::string_view bucket_name,
|
|
120
|
+
std::atomic_bool& logged)
|
|
121
|
+
{
|
|
122
|
+
if (unplaceable_replicas(config) == 0 || logged.exchange(true)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
CB_LOG_WARNING("bucket \"{}\" is configured with {} replica(s), but {} data node(s) can hold "
|
|
126
|
+
"only {} replica(s)",
|
|
127
|
+
logger::metadata(bucket_name),
|
|
128
|
+
config.num_replicas.value_or(0),
|
|
129
|
+
data_node_count(config),
|
|
130
|
+
placeable_replicas(config));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// True once every vbucket has its active and the placeable replica copies assigned to a node. A
|
|
134
|
+
// freshly created bucket reports an empty/partial vbucket map (replica slots set to -1) until the
|
|
135
|
+
// server finishes placing replicas; durable (MAJORITY) writes are ambiguous until then.
|
|
136
|
+
auto
|
|
137
|
+
vbucket_map_ready(const topology::configuration& config) -> bool
|
|
138
|
+
{
|
|
139
|
+
if (!config.vbmap.has_value()) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
const auto& vbmap = config.vbmap.value();
|
|
143
|
+
if (vbmap.empty()) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
// active + replicas
|
|
147
|
+
const auto copies = static_cast<std::size_t>(placeable_replicas(config)) + 1;
|
|
148
|
+
for (const auto& chain : vbmap) {
|
|
149
|
+
if (chain.size() < copies) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
for (std::size_t i = 0; i < copies; ++i) {
|
|
153
|
+
if (chain[i] < 0) { // -1 => copy not yet assigned to a node
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
namespace
|
|
162
|
+
{
|
|
163
|
+
// Self-owning async poll loop. Exactly one continuation (config fetch, ping, or the poll timer) is
|
|
164
|
+
// in flight at a time, each holding a shared_ptr to keep the operation alive; every continuation
|
|
165
|
+
// returns to poll()/schedule_retry(), which re-check the deadline, so the whole operation is
|
|
166
|
+
// bounded by `timeout` without a separate deadline timer.
|
|
167
|
+
class wait_until_ready_operation : public std::enable_shared_from_this<wait_until_ready_operation>
|
|
168
|
+
{
|
|
169
|
+
public:
|
|
170
|
+
wait_until_ready_operation(core::cluster core,
|
|
171
|
+
std::optional<std::string> bucket_name,
|
|
172
|
+
std::chrono::milliseconds timeout,
|
|
173
|
+
couchbase::cluster_state desired_state,
|
|
174
|
+
std::set<service_type> services,
|
|
175
|
+
utils::movable_function<void(std::error_code)> handler)
|
|
176
|
+
: core_{ std::move(core) }
|
|
177
|
+
, bucket_name_{ std::move(bucket_name) }
|
|
178
|
+
, deadline_{ std::chrono::steady_clock::now() + timeout }
|
|
179
|
+
, desired_state_{ desired_state }
|
|
180
|
+
, services_{ std::move(services) }
|
|
181
|
+
, handler_{ std::move(handler) }
|
|
182
|
+
, timer_{ core_.io_context() }
|
|
183
|
+
{
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
wait_until_ready_operation(const wait_until_ready_operation&) = delete;
|
|
187
|
+
wait_until_ready_operation(wait_until_ready_operation&&) = delete;
|
|
188
|
+
auto operator=(const wait_until_ready_operation&) -> wait_until_ready_operation& = delete;
|
|
189
|
+
auto operator=(wait_until_ready_operation&&) -> wait_until_ready_operation& = delete;
|
|
190
|
+
|
|
191
|
+
~wait_until_ready_operation()
|
|
192
|
+
{
|
|
193
|
+
// Fail closed. If the io_context is torn down while the wait is in flight, it drops every
|
|
194
|
+
// pending continuation (the poll timer, the config wait, the ping) without invoking their
|
|
195
|
+
// handlers; the last shared_ptr then expires here with the operation never completed. Deliver
|
|
196
|
+
// an error so a std::future caller never observes broken_promise and a callback caller never
|
|
197
|
+
// hangs. By the time the destructor runs the last reference is gone, so no other thread can be
|
|
198
|
+
// in complete() concurrently. The handler is user code, and a destructor must not let an
|
|
199
|
+
// exception escape.
|
|
200
|
+
if (!completed_) {
|
|
201
|
+
try {
|
|
202
|
+
handler_(errc::network::cluster_closed);
|
|
203
|
+
} catch (...) { // NOLINT(bugprone-empty-catch)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
void run()
|
|
209
|
+
{
|
|
210
|
+
// close() completes a wait still in flight: the public cluster stops its io_context once
|
|
211
|
+
// close()'s handler runs, so no timer or posted continuation of this wait would run after
|
|
212
|
+
// that. A cluster already closed completes here, inline, as open_bucket() does.
|
|
213
|
+
const std::weak_ptr<wait_until_ready_operation> weak_for_close = shared_from_this();
|
|
214
|
+
if (!core_.add_close_hook(weak_for_close, [weak_for_close]() {
|
|
215
|
+
if (auto self = weak_for_close.lock(); self) {
|
|
216
|
+
self->complete(errc::network::cluster_closed);
|
|
217
|
+
}
|
|
218
|
+
})) {
|
|
219
|
+
return complete(errc::network::cluster_closed);
|
|
220
|
+
}
|
|
221
|
+
if (desired_state_ == couchbase::cluster_state::offline) {
|
|
222
|
+
// Reject on the io_context, not inline, so the handler runs on the io thread as on the poll
|
|
223
|
+
// path. A closed cluster is answered inline above, because its io_context may be stopped.
|
|
224
|
+
auto self = shared_from_this();
|
|
225
|
+
return asio::post(core_.io_context(), [self]() {
|
|
226
|
+
self->complete(errc::common::invalid_argument);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
if (core_.is_protostellar()) {
|
|
230
|
+
// couchbase2 reaches every service through the gateway's gRPC channel, so a READY channel is
|
|
231
|
+
// both the online and the degraded state, whatever the bucket and services. There is no
|
|
232
|
+
// bucket to open and no session to ping. Posted for the same reason as the rejection above.
|
|
233
|
+
auto self = shared_from_this();
|
|
234
|
+
return asio::post(core_.io_context(), [self]() {
|
|
235
|
+
self->poll();
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
if (bucket_name_) {
|
|
239
|
+
// Opening the bucket is what makes its configuration (and therefore the vbucket map we poll)
|
|
240
|
+
// available on the core cluster. It is idempotent for an already-open bucket.
|
|
241
|
+
//
|
|
242
|
+
// Capture a weak_ptr, not self: open_bucket parks the callback in the core's deferred queue
|
|
243
|
+
// until the bucket opens, and the operation holds a strong core_ member -- a strong capture
|
|
244
|
+
// would form an op<->core reference cycle that survives cluster close, so the operation would
|
|
245
|
+
// never be destroyed and its handler never fires. anchor() keeps the operation alive on the
|
|
246
|
+
// io_context while the open is outstanding instead.
|
|
247
|
+
const auto& bucket_name = *bucket_name_;
|
|
248
|
+
anchor();
|
|
249
|
+
const std::weak_ptr<wait_until_ready_operation> weak = shared_from_this();
|
|
250
|
+
core_.open_bucket(bucket_name, [weak](std::error_code ec) {
|
|
251
|
+
auto self = weak.lock();
|
|
252
|
+
if (!self) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
self->timer_.cancel();
|
|
256
|
+
// A closed cluster will never open the bucket; fail fast with the accurate error instead of
|
|
257
|
+
// polling until the deadline (ping() returns an empty report once stopped, so the poll loop
|
|
258
|
+
// would otherwise surface unambiguous_timeout). Other open errors (e.g. a missing bucket)
|
|
259
|
+
// are transient from the caller's point of view and still resolve through the poll loop.
|
|
260
|
+
if (ec == errc::network::cluster_closed) {
|
|
261
|
+
return self->complete(ec);
|
|
262
|
+
}
|
|
263
|
+
self->poll();
|
|
264
|
+
});
|
|
265
|
+
} else {
|
|
266
|
+
poll();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private:
|
|
271
|
+
auto remaining() const -> std::chrono::milliseconds
|
|
272
|
+
{
|
|
273
|
+
const auto left = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
274
|
+
deadline_ - std::chrono::steady_clock::now());
|
|
275
|
+
return std::max(left, std::chrono::milliseconds::zero());
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// The close hook can run on another io thread than a continuation of this wait.
|
|
279
|
+
void complete(std::error_code ec)
|
|
280
|
+
{
|
|
281
|
+
if (completed_.exchange(true)) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
// Moved out, so that the handler's captures are released when it returns: a completed wait can
|
|
285
|
+
// stay referenced by a continuation queued on a stopped io_context.
|
|
286
|
+
auto handler = std::move(handler_);
|
|
287
|
+
handler(ec);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
void schedule_retry()
|
|
291
|
+
{
|
|
292
|
+
if (std::chrono::steady_clock::now() >= deadline_) {
|
|
293
|
+
return complete(errc::common::unambiguous_timeout);
|
|
294
|
+
}
|
|
295
|
+
// Never sleep past the deadline: when less than a full interval remains, a fixed poll_interval
|
|
296
|
+
// wait would overshoot the caller's timeout by up to one interval before poll() notices.
|
|
297
|
+
timer_.expires_after(std::min(poll_interval, remaining()));
|
|
298
|
+
auto self = shared_from_this();
|
|
299
|
+
timer_.async_wait([self](std::error_code timer_ec) {
|
|
300
|
+
if (timer_ec) {
|
|
301
|
+
return; // cancelled / io_context shutting down
|
|
302
|
+
}
|
|
303
|
+
self->poll();
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Keep the operation alive on the io_context (via timer_, holding a strong self) while a core
|
|
308
|
+
// call that captures only a weak_ptr is outstanding -- open_bucket and ping park their callbacks
|
|
309
|
+
// in the core's queues, so anchoring here rather than through those callbacks is what avoids the
|
|
310
|
+
// op<->core reference cycle. The anchor doubles as the deadline guard for the outstanding call:
|
|
311
|
+
// its continuation cancels the anchor, and if the deadline is reached first the wait completes
|
|
312
|
+
// with a timeout instead of stalling until the call happens to return.
|
|
313
|
+
void anchor()
|
|
314
|
+
{
|
|
315
|
+
timer_.expires_after(remaining());
|
|
316
|
+
timer_.async_wait([self = shared_from_this()](std::error_code timer_ec) {
|
|
317
|
+
if (timer_ec) {
|
|
318
|
+
return; // cancelled by the core call's continuation, or io_context shutting down
|
|
319
|
+
}
|
|
320
|
+
self->complete(errc::common::unambiguous_timeout);
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
void poll()
|
|
325
|
+
{
|
|
326
|
+
if (completed_) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (remaining() == std::chrono::milliseconds::zero()) {
|
|
330
|
+
return complete(errc::common::unambiguous_timeout);
|
|
331
|
+
}
|
|
332
|
+
if (core_.is_protostellar()) {
|
|
333
|
+
const auto [ec, ready] = core_.protostellar_channel_ready();
|
|
334
|
+
if (ec) {
|
|
335
|
+
return complete(ec);
|
|
336
|
+
}
|
|
337
|
+
if (ready) {
|
|
338
|
+
return complete({});
|
|
339
|
+
}
|
|
340
|
+
return schedule_retry();
|
|
341
|
+
}
|
|
342
|
+
if (bucket_name_ && desired_state_ == couchbase::cluster_state::online) {
|
|
343
|
+
// KV readiness (replica placement) first -- it is the condition durable writes need and the
|
|
344
|
+
// one a fresh bucket fails; then confirm service health with a ping. Only gate on it for the
|
|
345
|
+
// `online` target: `degraded` deliberately tolerates partial availability, so requiring full
|
|
346
|
+
// replica placement there would contradict the state's meaning and could never be satisfied
|
|
347
|
+
// by a genuinely degraded bucket.
|
|
348
|
+
const auto& bucket_name = *bucket_name_;
|
|
349
|
+
auto self = shared_from_this();
|
|
350
|
+
// Bound each waiter to one poll interval, not the whole remaining budget: when a config never
|
|
351
|
+
// arrives, a per-poll waiter armed for `remaining()` would leave one pending config callback
|
|
352
|
+
// and steady_timer stacked up for every 100ms tick until the deadline. A short inner timeout
|
|
353
|
+
// clears each waiter before the next poll (a cached config still fires its callback promptly,
|
|
354
|
+
// so this adds no latency to the common ready path). The overall deadline is still enforced
|
|
355
|
+
// by remaining() at the top of poll()/schedule_retry().
|
|
356
|
+
with_bucket_config_or_timeout<bool>(
|
|
357
|
+
core_,
|
|
358
|
+
bucket_name,
|
|
359
|
+
std::min(remaining(), poll_interval),
|
|
360
|
+
[self](couchbase::error err, bool ready) {
|
|
361
|
+
// A closed cluster is terminal: complete with the accurate error rather than retrying
|
|
362
|
+
// until the deadline turns it into unambiguous_timeout. Any other error (including the
|
|
363
|
+
// helper's own inner unambiguous_timeout when no config has arrived yet) just means "not
|
|
364
|
+
// ready yet" and is retried.
|
|
365
|
+
if (err.ec() == errc::network::cluster_closed) {
|
|
366
|
+
return self->complete(err.ec());
|
|
367
|
+
}
|
|
368
|
+
if (err.ec() || !ready) {
|
|
369
|
+
return self->schedule_retry();
|
|
370
|
+
}
|
|
371
|
+
self->do_ping();
|
|
372
|
+
},
|
|
373
|
+
// Weak: the helper parks this resolver in the bucket's deferred queue, which must not pin
|
|
374
|
+
// the operation and its handler.
|
|
375
|
+
[weak = weak_from_this()](const std::shared_ptr<topology::configuration>& config)
|
|
376
|
+
-> std::pair<std::error_code, bool> {
|
|
377
|
+
auto self = weak.lock();
|
|
378
|
+
if (!self || !config) {
|
|
379
|
+
return { {}, false };
|
|
380
|
+
}
|
|
381
|
+
warn_if_replicas_unplaceable(
|
|
382
|
+
*config, self->bucket_name_.value_or(""), self->unplaceable_replicas_logged_);
|
|
383
|
+
return { {}, vbucket_map_ready(*config) };
|
|
384
|
+
});
|
|
385
|
+
} else {
|
|
386
|
+
do_ping();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
void do_ping()
|
|
391
|
+
{
|
|
392
|
+
// Weak capture + anchor, for the same reason as open_bucket: core_.ping parks the callback in
|
|
393
|
+
// the core, so a strong self-capture would cycle with the core_ member and leak on close.
|
|
394
|
+
anchor();
|
|
395
|
+
const std::weak_ptr<wait_until_ready_operation> weak = shared_from_this();
|
|
396
|
+
core_.ping(
|
|
397
|
+
std::nullopt, bucket_name_, services_, remaining(), [weak](const diag::ping_result& report) {
|
|
398
|
+
auto self = weak.lock();
|
|
399
|
+
if (!self) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
self->timer_.cancel();
|
|
403
|
+
if (ping_predicate_satisfied(report, self->desired_state_, self->services_)) {
|
|
404
|
+
return self->complete({});
|
|
405
|
+
}
|
|
406
|
+
self->schedule_retry();
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
core::cluster core_;
|
|
411
|
+
std::optional<std::string> bucket_name_;
|
|
412
|
+
std::chrono::steady_clock::time_point deadline_;
|
|
413
|
+
couchbase::cluster_state desired_state_;
|
|
414
|
+
std::set<service_type> services_;
|
|
415
|
+
utils::movable_function<void(std::error_code)> handler_;
|
|
416
|
+
asio::steady_timer timer_;
|
|
417
|
+
std::atomic_bool completed_{ false };
|
|
418
|
+
// Every poll sees the same configuration until a node is added, so the warning is logged once.
|
|
419
|
+
std::atomic_bool unplaceable_replicas_logged_{ false };
|
|
420
|
+
};
|
|
421
|
+
} // namespace
|
|
422
|
+
|
|
423
|
+
void
|
|
424
|
+
wait_until_ready(core::cluster core,
|
|
425
|
+
std::optional<std::string> bucket_name,
|
|
426
|
+
std::chrono::milliseconds timeout,
|
|
427
|
+
couchbase::cluster_state desired_state,
|
|
428
|
+
std::set<service_type> services,
|
|
429
|
+
utils::movable_function<void(std::error_code)> handler)
|
|
430
|
+
{
|
|
431
|
+
auto op = std::make_shared<wait_until_ready_operation>(std::move(core),
|
|
432
|
+
std::move(bucket_name),
|
|
433
|
+
timeout,
|
|
434
|
+
desired_state,
|
|
435
|
+
std::move(services),
|
|
436
|
+
std::move(handler));
|
|
437
|
+
op->run();
|
|
438
|
+
}
|
|
439
|
+
} // namespace couchbase::core::impl
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
2
|
+
/*
|
|
3
|
+
* Copyright 2026-Present Couchbase, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#pragma once
|
|
19
|
+
|
|
20
|
+
#include "core/cluster.hxx"
|
|
21
|
+
#include "core/diagnostics.hxx"
|
|
22
|
+
#include "core/service_type.hxx"
|
|
23
|
+
#include "core/topology/configuration.hxx"
|
|
24
|
+
#include "core/utils/movable_function.hxx"
|
|
25
|
+
|
|
26
|
+
#include <couchbase/cluster_state.hxx>
|
|
27
|
+
|
|
28
|
+
#include <atomic>
|
|
29
|
+
#include <chrono>
|
|
30
|
+
#include <cstdint>
|
|
31
|
+
#include <optional>
|
|
32
|
+
#include <set>
|
|
33
|
+
#include <string>
|
|
34
|
+
#include <string_view>
|
|
35
|
+
#include <system_error>
|
|
36
|
+
|
|
37
|
+
namespace couchbase::core::impl
|
|
38
|
+
{
|
|
39
|
+
/**
|
|
40
|
+
* Returns whether @p report satisfies @p state for the @p requested services.
|
|
41
|
+
*
|
|
42
|
+
* online : every requested service has at least one endpoint and ALL of them are ok.
|
|
43
|
+
* degraded : every requested service has at least one endpoint that is ok.
|
|
44
|
+
*
|
|
45
|
+
* When @p requested is empty, whichever services the report contains are considered; an empty
|
|
46
|
+
* report (nothing pinged back yet) is treated as not-ready. Exposed for unit testing.
|
|
47
|
+
*/
|
|
48
|
+
auto
|
|
49
|
+
ping_predicate_satisfied(const diag::ping_result& report,
|
|
50
|
+
couchbase::cluster_state state,
|
|
51
|
+
const std::set<service_type>& requested) -> bool;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Returns the number of replica copies of each vbucket the data nodes can hold: num_replicas,
|
|
55
|
+
* capped at one per data node besides the one holding the active. The server leaves the remaining
|
|
56
|
+
* replica slots at -1 for as long as no node is added. A configuration listing no data node is not
|
|
57
|
+
* capped. Exposed for unit testing.
|
|
58
|
+
*/
|
|
59
|
+
auto
|
|
60
|
+
placeable_replicas(const topology::configuration& config) -> std::uint32_t;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Returns how many of the configured replicas the data nodes cannot hold. Exposed for unit testing.
|
|
64
|
+
*/
|
|
65
|
+
auto
|
|
66
|
+
unplaceable_replicas(const topology::configuration& config) -> std::uint32_t;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Logs a warning naming @p bucket_name when unplaceable_replicas() is non-zero and @p logged is
|
|
70
|
+
* still false, then sets @p logged. Exposed for unit testing.
|
|
71
|
+
*/
|
|
72
|
+
void
|
|
73
|
+
warn_if_replicas_unplaceable(const topology::configuration& config,
|
|
74
|
+
std::string_view bucket_name,
|
|
75
|
+
std::atomic_bool& logged);
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Returns true once every vbucket has its active and placeable_replicas() replica copies assigned
|
|
79
|
+
* to a node. A freshly created bucket reports an empty/partial vbucket map (replica slots set to
|
|
80
|
+
* -1) until the server finishes placing replicas; durable (MAJORITY) writes are ambiguous until
|
|
81
|
+
* then. Exposed for unit testing.
|
|
82
|
+
*/
|
|
83
|
+
auto
|
|
84
|
+
vbucket_map_ready(const topology::configuration& config) -> bool;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Polls the cluster (or, when @p bucket_name is set, a bucket) until it reaches @p desired_state or
|
|
88
|
+
* @p timeout elapses, then invokes @p handler exactly once with the resulting error code (empty on
|
|
89
|
+
* success, @c errc::common::unambiguous_timeout on timeout, @c errc::common::invalid_argument when
|
|
90
|
+
* @p desired_state is @c cluster_state::offline, @c errc::network::cluster_closed when the
|
|
91
|
+
* io_context is torn down while the wait is in flight).
|
|
92
|
+
*
|
|
93
|
+
* Readiness is assessed with @c cluster::ping(). When @p desired_state is @c cluster_state::online
|
|
94
|
+
* the bucket variant additionally waits until every vbucket has its active and
|
|
95
|
+
* placeable_replicas() replica copies assigned -- the readiness that durable writes require and
|
|
96
|
+
* that a freshly created bucket does not satisfy immediately. Replicas beyond what the data nodes
|
|
97
|
+
* can hold are not awaited, and warn_if_replicas_unplaceable() logs them once per wait. The @c
|
|
98
|
+
* degraded target is ping-only, since it deliberately tolerates partial availability.
|
|
99
|
+
*
|
|
100
|
+
* The operation is fully asynchronous: it owns itself for its lifetime and drives the poll cadence
|
|
101
|
+
* with an asio timer on the cluster's io_context; it does not block a thread.
|
|
102
|
+
*/
|
|
103
|
+
void
|
|
104
|
+
wait_until_ready(core::cluster core,
|
|
105
|
+
std::optional<std::string> bucket_name,
|
|
106
|
+
std::chrono::milliseconds timeout,
|
|
107
|
+
couchbase::cluster_state desired_state,
|
|
108
|
+
std::set<service_type> services,
|
|
109
|
+
utils::movable_function<void(std::error_code)> handler);
|
|
110
|
+
} // namespace couchbase::core::impl
|