couchbase 3.8.1 → 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 +2 -2
- 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 +214 -155
- 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 +325 -25
- 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 +66 -0
- 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 +316 -317
- 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 +275 -14
- data/ext/couchbase/core/io/streams.hxx +53 -0
- 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/bucket_create.cxx +37 -38
- data/ext/couchbase/core/operations/management/bucket_update.cxx +26 -28
- 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 +362 -241
- data/ext/couchbase/core/transactions/attempt_context_impl.hxx +24 -3
- 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 +194 -66
- 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 +15 -4
- 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 +216 -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 +33 -14
- 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 +181 -44
- 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 +92 -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
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
#include "bucket.hxx"
|
|
19
19
|
|
|
20
|
+
#include "bucket_unit_test_api.hxx"
|
|
21
|
+
|
|
20
22
|
#include "collection_id_cache_entry.hxx"
|
|
21
23
|
#include "core/app_telemetry_meter.hxx"
|
|
22
24
|
#include "core/cluster_options.hxx"
|
|
@@ -26,6 +28,7 @@
|
|
|
26
28
|
#include "core/error_context/key_value_status_code.hxx"
|
|
27
29
|
#include "core/io/mcbp_message.hxx"
|
|
28
30
|
#include "core/logger/logger.hxx"
|
|
31
|
+
#include "core/logger/redaction.hxx"
|
|
29
32
|
#include "core/mcbp/codec.hxx"
|
|
30
33
|
#include "core/metrics/meter_wrapper.hxx"
|
|
31
34
|
#include "core/protocol/client_opcode.hxx"
|
|
@@ -33,6 +36,7 @@
|
|
|
33
36
|
#include "core/protocol/hello_feature.hxx"
|
|
34
37
|
#include "core/response_handler.hxx"
|
|
35
38
|
#include "core/service_type.hxx"
|
|
39
|
+
#include "core/tracing/constants.hxx"
|
|
36
40
|
#include "core/tracing/tracer_wrapper.hxx"
|
|
37
41
|
#include "core/utils/movable_function.hxx"
|
|
38
42
|
#include "dispatcher.hxx"
|
|
@@ -45,6 +49,7 @@
|
|
|
45
49
|
#include "protocol/cmd_get_cluster_config.hxx"
|
|
46
50
|
#include "retry_orchestrator.hxx"
|
|
47
51
|
|
|
52
|
+
#include <couchbase/build_config.hxx>
|
|
48
53
|
#include <couchbase/error_codes.hxx>
|
|
49
54
|
#include <couchbase/retry_reason.hxx>
|
|
50
55
|
#include <couchbase/retry_strategy.hxx>
|
|
@@ -94,7 +99,7 @@ public:
|
|
|
94
99
|
tls_context_provider& tls)
|
|
95
100
|
: client_id_{ std::move(client_id) }
|
|
96
101
|
, name_{ std::move(name) }
|
|
97
|
-
, log_prefix_{ fmt::format("[{}/{}]", client_id_, name_) }
|
|
102
|
+
, log_prefix_{ fmt::format("[{}/{}]", client_id_, logger::metadata(name_)) }
|
|
98
103
|
, tracer_{ std::move(tracer) }
|
|
99
104
|
, meter_{ std::move(meter) }
|
|
100
105
|
, orphan_reporter_{ std::move(orphan_reporter) }
|
|
@@ -122,7 +127,11 @@ public:
|
|
|
122
127
|
// TODO(SA): copy from mcbp_command, subject to refactor later
|
|
123
128
|
#ifdef COUCHBASE_CXX_CLIENT_CREATE_OPERATION_SPAN_IN_CORE
|
|
124
129
|
metrics::metric_attributes attrs{
|
|
125
|
-
|
|
130
|
+
tracing::service::key_value,
|
|
131
|
+
fmt::format("{}", req->command_),
|
|
132
|
+
metrics::standardized_error_type(ec),
|
|
133
|
+
name_,
|
|
134
|
+
req->scope_name_,
|
|
126
135
|
req->collection_name_,
|
|
127
136
|
};
|
|
128
137
|
meter_->record_value(std::move(attrs), req->dispatched_time_);
|
|
@@ -141,8 +150,13 @@ public:
|
|
|
141
150
|
// self->span_->add_tag(tracing::attributes::orphan, "canceled");
|
|
142
151
|
return req->try_callback(resp, ec);
|
|
143
152
|
}
|
|
144
|
-
backoff_and_retry(
|
|
145
|
-
|
|
153
|
+
if (!backoff_and_retry(req,
|
|
154
|
+
reason == retry_reason::do_not_retry ? retry_reason::node_not_available
|
|
155
|
+
: reason)) {
|
|
156
|
+
// The retry was refused, so nothing else will carry this request: complete it here, as
|
|
157
|
+
// every other caller of backoff_and_retry() does.
|
|
158
|
+
req->try_callback(resp, ec);
|
|
159
|
+
}
|
|
146
160
|
return;
|
|
147
161
|
}
|
|
148
162
|
key_value_status_code status{ key_value_status_code::unknown };
|
|
@@ -157,6 +171,58 @@ public:
|
|
|
157
171
|
reason = retry_reason::key_value_error_map_retry_indicated;
|
|
158
172
|
} else {
|
|
159
173
|
switch (status) {
|
|
174
|
+
case key_value_status_code::unknown_collection:
|
|
175
|
+
/*
|
|
176
|
+
* SDK-RFC 0049 (SDK3 Retry Handling), section "Response Code Retry List":
|
|
177
|
+
* > Explicitly excluded here is the GetCollectionID KV command, since it returns 0x88 if
|
|
178
|
+
* > the collection is not found.
|
|
179
|
+
*
|
|
180
|
+
* The request completes with collection_not_found.
|
|
181
|
+
*/
|
|
182
|
+
if (req->command_ == protocol::client_opcode::get_collection_id) {
|
|
183
|
+
reason = retry_reason::do_not_retry;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* The server does not recognise the collection id we sent. This covers two cases:
|
|
188
|
+
*
|
|
189
|
+
* 1. The collection was dropped and recreated, so its id changed. The cached id we sent
|
|
190
|
+
* is now stale and re-sending it would loop forever. We must invalidate the cache
|
|
191
|
+
* and re-resolve the id via GetCollectionID before retrying.
|
|
192
|
+
* 2. The node's collection manifest simply lags ns_server's view: the id is still valid
|
|
193
|
+
* but has not propagated to this particular KV node yet. Here re-sending the same id
|
|
194
|
+
* succeeds once the manifest catches up, bounded by the operation timeout.
|
|
195
|
+
*
|
|
196
|
+
* Prefer re-resolution: if the request was dispatched through the collection-id cache the
|
|
197
|
+
* hook invalidates the cached id and schedules a re-resolution retry (covering case 1,
|
|
198
|
+
* and case 2 too since GetCollectionID re-fetches). This matches the legacy mcbp_command
|
|
199
|
+
* path, which handled both via handle_unknown_collection(); when KV ops were migrated to
|
|
200
|
+
* this path that handling was dropped and the status surfaced directly as
|
|
201
|
+
* collection_not_found, which consumers such as the range scan orchestrator treat as
|
|
202
|
+
* fatal.
|
|
203
|
+
*
|
|
204
|
+
* If there is no hook (request bypassed the cid cache) or re-resolution gave up, fall
|
|
205
|
+
* back to a bounded collection-outdated retry with the same id, which still recovers
|
|
206
|
+
* case 2.
|
|
207
|
+
*/
|
|
208
|
+
if (req->on_unknown_collection_ && req->on_unknown_collection_()) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
reason = retry_reason::key_value_collection_outdated;
|
|
212
|
+
break;
|
|
213
|
+
case key_value_status_code::config_only:
|
|
214
|
+
/**
|
|
215
|
+
* The node has a configuration but is not serving data operations yet (e.g. during
|
|
216
|
+
* warmup or rebalance). Mirror the legacy path: fetch a fresh configuration and retry so
|
|
217
|
+
* the request is routed to a node that can serve it.
|
|
218
|
+
*/
|
|
219
|
+
CB_LOG_DEBUG(
|
|
220
|
+
"{} server returned config_only status, meaning that the node does not serve "
|
|
221
|
+
"data operations, requesting new configuration and retrying",
|
|
222
|
+
log_prefix_);
|
|
223
|
+
fetch_config();
|
|
224
|
+
reason = retry_reason::service_response_code_indicated;
|
|
225
|
+
break;
|
|
160
226
|
case key_value_status_code::locked:
|
|
161
227
|
if (req->command_ != protocol::client_opcode::unlock) {
|
|
162
228
|
/**
|
|
@@ -185,6 +251,7 @@ public:
|
|
|
185
251
|
}
|
|
186
252
|
|
|
187
253
|
void handle_response(std::shared_ptr<mcbp::queue_request> req,
|
|
254
|
+
std::optional<io::mcbp_session> /* session */,
|
|
188
255
|
std::error_code error,
|
|
189
256
|
retry_reason reason,
|
|
190
257
|
io::mcbp_message msg,
|
|
@@ -213,21 +280,38 @@ public:
|
|
|
213
280
|
if (ec == errc::common::request_canceled) {
|
|
214
281
|
return req->cancel(ec);
|
|
215
282
|
}
|
|
216
|
-
|
|
283
|
+
// This runs after direct_dispatch() returned, so its error code has nobody left to report
|
|
284
|
+
// to. Complete the request here rather than leave it to its deadline.
|
|
285
|
+
if (auto rc = self->direct_dispatch(req); rc) {
|
|
286
|
+
req->try_callback({}, rc);
|
|
287
|
+
}
|
|
217
288
|
});
|
|
218
289
|
}
|
|
219
290
|
|
|
220
291
|
req->dispatched_time_ = std::chrono::steady_clock::now();
|
|
221
292
|
|
|
222
|
-
auto
|
|
223
|
-
if (!session || !session->has_config()) {
|
|
224
|
-
|
|
293
|
+
auto routed = route_request(req);
|
|
294
|
+
if (!routed.session || !routed.session->has_config()) {
|
|
295
|
+
if (!routed.connect_pending) {
|
|
296
|
+
// No bootstrap was started for this node, and only a bootstrap
|
|
297
|
+
// completion or a bucket close drains the deferred queue. Deferring
|
|
298
|
+
// here would leave the request waiting for its deadline, so retry
|
|
299
|
+
// instead: that re-routes against the current configuration.
|
|
300
|
+
if (backoff_and_retry(req, retry_reason::node_not_available)) {
|
|
301
|
+
return {};
|
|
302
|
+
}
|
|
303
|
+
return errc::common::service_not_available;
|
|
304
|
+
}
|
|
305
|
+
return defer_command([self = shared_from_this(), req](std::error_code ec) {
|
|
225
306
|
if (ec == errc::common::request_canceled) {
|
|
226
307
|
return req->cancel(ec);
|
|
227
308
|
}
|
|
228
|
-
self->direct_dispatch(
|
|
309
|
+
if (auto rc = self->direct_dispatch(req); rc) {
|
|
310
|
+
req->try_callback({}, rc);
|
|
311
|
+
}
|
|
229
312
|
});
|
|
230
313
|
}
|
|
314
|
+
auto session = routed.session;
|
|
231
315
|
if (session->is_stopped()) {
|
|
232
316
|
if (backoff_and_retry(req, retry_reason::node_not_available)) {
|
|
233
317
|
return {};
|
|
@@ -244,7 +328,7 @@ public:
|
|
|
244
328
|
{
|
|
245
329
|
auto handle_error = [is_retry, req](std::error_code ec) {
|
|
246
330
|
// We only want to log an error on retries if the error isn't cancelled.
|
|
247
|
-
if (!is_retry ||
|
|
331
|
+
if (!is_retry || ec != errc::common::request_canceled) {
|
|
248
332
|
CB_LOG_ERROR("reschedule failed, failing request ({})", ec.message());
|
|
249
333
|
}
|
|
250
334
|
|
|
@@ -253,15 +337,29 @@ public:
|
|
|
253
337
|
|
|
254
338
|
CB_LOG_DEBUG("request being re-queued. opaque={}, opcode={}", req->opaque_, req->command_);
|
|
255
339
|
|
|
256
|
-
auto
|
|
257
|
-
if (!session || !session->has_config()) {
|
|
258
|
-
|
|
340
|
+
auto routed = route_request(req);
|
|
341
|
+
if (!routed.session || !routed.session->has_config()) {
|
|
342
|
+
if (!routed.connect_pending) {
|
|
343
|
+
// As in direct_dispatch(): no bootstrap is pending for this node, so a
|
|
344
|
+
// deferred request would wait for its deadline instead of being sent.
|
|
345
|
+
if (backoff_and_retry(req, retry_reason::node_not_available)) {
|
|
346
|
+
return {};
|
|
347
|
+
}
|
|
348
|
+
// A re-queue is driven by a backoff timer that ignores what this
|
|
349
|
+
// returns, so the request has to be completed here.
|
|
350
|
+
handle_error(errc::common::service_not_available);
|
|
351
|
+
return errc::common::service_not_available;
|
|
352
|
+
}
|
|
353
|
+
return defer_command([self = shared_from_this(), req, handle_error](std::error_code ec) {
|
|
259
354
|
if (ec == errc::common::request_canceled) {
|
|
260
355
|
return req->cancel(ec);
|
|
261
356
|
}
|
|
262
|
-
self->direct_dispatch(req);
|
|
357
|
+
if (auto rc = self->direct_dispatch(req); rc) {
|
|
358
|
+
handle_error(rc);
|
|
359
|
+
}
|
|
263
360
|
});
|
|
264
361
|
}
|
|
362
|
+
auto session = routed.session;
|
|
265
363
|
if (session->is_stopped()) {
|
|
266
364
|
if (backoff_and_retry(req, retry_reason::node_not_available)) {
|
|
267
365
|
return {};
|
|
@@ -302,23 +400,55 @@ public:
|
|
|
302
400
|
-> bool
|
|
303
401
|
{
|
|
304
402
|
auto action = retry_orchestrator::should_retry(request, reason);
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
403
|
+
if (!action.need_to_retry()) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Park the request where close() can reach it. The timer below is the only
|
|
408
|
+
// thing that would re-dispatch it, and asio abandons a pending wait when the
|
|
409
|
+
// io_context goes away rather than completing it, so a request reachable only
|
|
410
|
+
// from that handler is neither completed nor released. A queue also lets
|
|
411
|
+
// cancel() take it back out, since a request de-queues from whichever queue
|
|
412
|
+
// holds it.
|
|
413
|
+
if (auto ec = retry_backoff_queue_.push(request, 0); ec) {
|
|
414
|
+
// Nothing will retry it: the bucket has closed, or it is already cancelled.
|
|
415
|
+
request->try_callback({}, errc::common::request_canceled);
|
|
416
|
+
return true;
|
|
316
417
|
}
|
|
317
|
-
|
|
418
|
+
|
|
419
|
+
auto timer = std::make_shared<asio::steady_timer>(ctx_);
|
|
420
|
+
timer->expires_after(action.duration());
|
|
421
|
+
timer->async_wait([self = shared_from_this(), request](auto error) {
|
|
422
|
+
if (!self->retry_backoff_queue_.remove(request)) {
|
|
423
|
+
// close() completed it, or it was cancelled: either way it is done.
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (error == asio::error::operation_aborted) {
|
|
427
|
+
request->try_callback({}, errc::common::request_canceled);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
self->direct_re_queue(request, true);
|
|
431
|
+
});
|
|
432
|
+
request->set_retry_backoff(timer);
|
|
433
|
+
return true;
|
|
318
434
|
}
|
|
319
435
|
|
|
320
|
-
|
|
321
|
-
|
|
436
|
+
struct routed_session {
|
|
437
|
+
std::optional<io::mcbp_session> session{};
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* False when no session exists for the node and no bootstrap was started
|
|
441
|
+
* for it, so nothing will drain the deferred queue. The caller must retry
|
|
442
|
+
* rather than defer; see connect_session().
|
|
443
|
+
*
|
|
444
|
+
* The default is false. route_request() returns a default-constructed
|
|
445
|
+
* value when the configuration names no server for the vbucket, and a
|
|
446
|
+
* request deferred on that has nothing to wait for.
|
|
447
|
+
*/
|
|
448
|
+
bool connect_pending{ false };
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
auto route_request(const std::shared_ptr<mcbp::queue_request>& req) -> routed_session
|
|
322
452
|
{
|
|
323
453
|
if (req->key_.empty()) {
|
|
324
454
|
if (auto server = server_by_vbucket(req->vbucket_, req->replica_index_); server) {
|
|
@@ -328,7 +458,7 @@ public:
|
|
|
328
458
|
req->vbucket_ = partition;
|
|
329
459
|
return find_or_connect_session_by_index(server.value());
|
|
330
460
|
}
|
|
331
|
-
return
|
|
461
|
+
return {};
|
|
332
462
|
}
|
|
333
463
|
|
|
334
464
|
[[nodiscard]] auto server_by_vbucket(std::uint16_t vbucket, std::size_t node_index)
|
|
@@ -351,6 +481,12 @@ public:
|
|
|
351
481
|
return { 0, std::nullopt };
|
|
352
482
|
}
|
|
353
483
|
|
|
484
|
+
[[nodiscard]] auto config_snapshot() const -> std::shared_ptr<topology::configuration>
|
|
485
|
+
{
|
|
486
|
+
const std::scoped_lock lock(config_mutex_);
|
|
487
|
+
return config_;
|
|
488
|
+
}
|
|
489
|
+
|
|
354
490
|
auto config_rev() const -> std::string
|
|
355
491
|
{
|
|
356
492
|
const std::scoped_lock lock(config_mutex_);
|
|
@@ -370,11 +506,33 @@ public:
|
|
|
370
506
|
return { 0, std::nullopt };
|
|
371
507
|
}
|
|
372
508
|
|
|
373
|
-
|
|
509
|
+
/**
|
|
510
|
+
* A command deferred for this index is only ever dispatched or completed by
|
|
511
|
+
* one of two events: a bootstrap finishing, or the bucket closing.
|
|
512
|
+
*
|
|
513
|
+
* @return true when one of them is coming. Either a bootstrap is now pending
|
|
514
|
+
* for this index, or the bucket is closed and defer_command() completes the
|
|
515
|
+
* command with a cancellation. False when neither is, in which case the
|
|
516
|
+
* caller must retry: a command deferred now would wait for its deadline.
|
|
517
|
+
*/
|
|
518
|
+
[[nodiscard]] auto connect_session(std::size_t index) -> bool
|
|
374
519
|
{
|
|
375
520
|
const std::scoped_lock lock(config_mutex_, sessions_mutex_);
|
|
376
521
|
if (!config_) {
|
|
377
|
-
return;
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
if (closed_) {
|
|
525
|
+
// Do not connect a session into a bucket that is being torn down: close() has already stopped
|
|
526
|
+
// sessions_ and will not run again, so a session added here would never be stopped and would
|
|
527
|
+
// strand the whole bucket/session graph as a pure cycle (see the closed_ guard in bootstrap()
|
|
528
|
+
// and update_config()).
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (index >= config_->nodes.size()) {
|
|
533
|
+
// The index was resolved against an earlier configuration and the node it
|
|
534
|
+
// named is no longer in the topology, so there is nothing to connect to.
|
|
535
|
+
return false;
|
|
378
536
|
}
|
|
379
537
|
|
|
380
538
|
const auto& node = config_->nodes[index];
|
|
@@ -383,7 +541,9 @@ public:
|
|
|
383
541
|
auto port = node.port_or(
|
|
384
542
|
origin_.options().network, service_type::key_value, origin_.options().enable_tls, 0);
|
|
385
543
|
if (port == 0) {
|
|
386
|
-
|
|
544
|
+
// The node is in the topology but advertises no key-value port, so there
|
|
545
|
+
// is no session to open for it either.
|
|
546
|
+
return false;
|
|
387
547
|
}
|
|
388
548
|
|
|
389
549
|
const couchbase::core::origin origin(origin_.credentials(), hostname, port, origin_.options());
|
|
@@ -393,13 +553,12 @@ public:
|
|
|
393
553
|
client_id_, node.node_uuid, ctx_, tls_, origin, state_listener_, name_, known_features_)
|
|
394
554
|
: io::mcbp_session(
|
|
395
555
|
client_id_, node.node_uuid, ctx_, origin, state_listener_, name_, known_features_);
|
|
396
|
-
CB_LOG_DEBUG(R"({} rev={}, connect idx={}, session="{}", address="{}
|
|
556
|
+
CB_LOG_DEBUG(R"({} rev={}, connect idx={}, session="{}", address="{}")",
|
|
397
557
|
log_prefix_,
|
|
398
558
|
config_->rev_str(),
|
|
399
559
|
node.index,
|
|
400
560
|
session.id(),
|
|
401
|
-
hostname,
|
|
402
|
-
port);
|
|
561
|
+
logger::system_data(fmt::format("{}:{}", hostname, port)));
|
|
403
562
|
session.bootstrap(
|
|
404
563
|
[self = shared_from_this(), session](std::error_code err,
|
|
405
564
|
topology::configuration cfg) mutable -> void {
|
|
@@ -415,6 +574,7 @@ public:
|
|
|
415
574
|
},
|
|
416
575
|
true);
|
|
417
576
|
sessions_.insert_or_assign(index, std::move(session));
|
|
577
|
+
return true;
|
|
418
578
|
}
|
|
419
579
|
|
|
420
580
|
void restart_sessions()
|
|
@@ -423,6 +583,13 @@ public:
|
|
|
423
583
|
if (!config_) {
|
|
424
584
|
return;
|
|
425
585
|
}
|
|
586
|
+
if (closed_) {
|
|
587
|
+
// remove_session() posts restart_sessions() whenever a session drops. If that post is
|
|
588
|
+
// serviced after close() has torn the bucket down, recreating sessions here would strand them
|
|
589
|
+
// (nothing stops a session added after close()), leaking the whole bucket/session graph as a
|
|
590
|
+
// pure cycle. close() sets closed_ before it swaps and stops sessions_, so bail out.
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
426
593
|
|
|
427
594
|
std::size_t kv_node_index{ 0 };
|
|
428
595
|
for (std::size_t index = 0; index < config_->nodes.size(); ++index) {
|
|
@@ -445,12 +612,11 @@ public:
|
|
|
445
612
|
if (auto found_kv_node_index = ptr->first; found_kv_node_index != kv_node_index) {
|
|
446
613
|
if (auto current = sessions_.find(kv_node_index); current == sessions_.end()) {
|
|
447
614
|
CB_LOG_WARNING(
|
|
448
|
-
R"({} KV node index mismatch: config rev={} states that address="{}
|
|
615
|
+
R"({} KV node index mismatch: config rev={} states that address="{}" should be at idx={}, )"
|
|
449
616
|
R"(but it is at idx={} ("{}"). Moving session to idx={}.)",
|
|
450
617
|
log_prefix_,
|
|
451
618
|
config_->rev_str(),
|
|
452
|
-
hostname,
|
|
453
|
-
port,
|
|
619
|
+
logger::system_data(fmt::format("{}:{}", hostname, port)),
|
|
454
620
|
kv_node_index,
|
|
455
621
|
found_kv_node_index,
|
|
456
622
|
ptr->second.id(),
|
|
@@ -459,17 +625,16 @@ public:
|
|
|
459
625
|
sessions_.erase(ptr);
|
|
460
626
|
} else {
|
|
461
627
|
CB_LOG_WARNING(
|
|
462
|
-
R"({} KV node index mismatch: config rev={} states that address="{}
|
|
628
|
+
R"({} KV node index mismatch: config rev={} states that address="{}" should be at idx={}, )"
|
|
463
629
|
R"(but it is at idx={} ("{}"). Slot with idx={} is holds session with address="{}" ("{}"), swapping them.)",
|
|
464
630
|
log_prefix_,
|
|
465
631
|
config_->rev_str(),
|
|
466
|
-
hostname,
|
|
467
|
-
port,
|
|
632
|
+
logger::system_data(fmt::format("{}:{}", hostname, port)),
|
|
468
633
|
kv_node_index,
|
|
469
634
|
found_kv_node_index,
|
|
470
635
|
ptr->second.id(),
|
|
471
636
|
kv_node_index,
|
|
472
|
-
current->second.bootstrap_address(),
|
|
637
|
+
logger::system_data(current->second.bootstrap_address()),
|
|
473
638
|
current->second.id());
|
|
474
639
|
std::swap(current->second, ptr->second);
|
|
475
640
|
}
|
|
@@ -491,13 +656,12 @@ public:
|
|
|
491
656
|
known_features_)
|
|
492
657
|
: io::mcbp_session(
|
|
493
658
|
client_id_, node.node_uuid, ctx_, origin, state_listener_, name_, known_features_);
|
|
494
|
-
CB_LOG_DEBUG(R"({} rev={}, restart idx={}, session="{}", address="{}
|
|
659
|
+
CB_LOG_DEBUG(R"({} rev={}, restart idx={}, session="{}", address="{}")",
|
|
495
660
|
log_prefix_,
|
|
496
661
|
config_->rev_str(),
|
|
497
662
|
node.index,
|
|
498
663
|
session.id(),
|
|
499
|
-
hostname,
|
|
500
|
-
port);
|
|
664
|
+
logger::system_data(fmt::format("{}:{}", hostname, port)));
|
|
501
665
|
session.bootstrap(
|
|
502
666
|
[self = shared_from_this(), session](std::error_code err,
|
|
503
667
|
topology::configuration cfg) mutable {
|
|
@@ -523,12 +687,11 @@ public:
|
|
|
523
687
|
const std::scoped_lock lock(sessions_mutex_);
|
|
524
688
|
for (auto ptr = sessions_.cbegin(); ptr != sessions_.cend();) {
|
|
525
689
|
if (ptr->second.id() == id) {
|
|
526
|
-
CB_LOG_DEBUG(R"({} removed session id="{}", address="{}", bootstrap_address="{}
|
|
690
|
+
CB_LOG_DEBUG(R"({} removed session id="{}", address="{}", bootstrap_address="{}")",
|
|
527
691
|
log_prefix_,
|
|
528
692
|
ptr->second.id(),
|
|
529
|
-
ptr->second.remote_address(),
|
|
530
|
-
ptr->second.
|
|
531
|
-
ptr->second.bootstrap_port());
|
|
693
|
+
logger::system_data(ptr->second.remote_address()),
|
|
694
|
+
logger::system_data(ptr->second.bootstrap_address()));
|
|
532
695
|
ptr = sessions_.erase(ptr);
|
|
533
696
|
found = true;
|
|
534
697
|
} else {
|
|
@@ -545,6 +708,17 @@ public:
|
|
|
545
708
|
|
|
546
709
|
void bootstrap(utils::movable_function<void(std::error_code, topology::configuration)>&& handler)
|
|
547
710
|
{
|
|
711
|
+
if (closed_) {
|
|
712
|
+
// The bucket was closed before this bootstrap even started -- e.g. the cluster was torn down
|
|
713
|
+
// in the window between cluster_impl::open_bucket() inserting the bucket into buckets_ and
|
|
714
|
+
// calling bootstrap() (the buckets_ lock is released in between). close() is one-shot and
|
|
715
|
+
// will not run again, so registering a config listener or starting a session here would never
|
|
716
|
+
// be undone: the session, its self-capturing bootstrap continuation, and this bucket would be
|
|
717
|
+
// stranded as a pure cycle that LeakSanitizer/Valgrind report as leaked, and the continuation
|
|
718
|
+
// (which a scan/ping/etc. may be parked behind) would never fire. Bail before touching any
|
|
719
|
+
// shared state.
|
|
720
|
+
return handler(errc::common::request_canceled, {});
|
|
721
|
+
}
|
|
548
722
|
if (state_listener_) {
|
|
549
723
|
state_listener_->register_config_listener(shared_from_this());
|
|
550
724
|
}
|
|
@@ -553,28 +727,68 @@ public:
|
|
|
553
727
|
? io::mcbp_session(
|
|
554
728
|
client_id_, {}, ctx_, tls_, origin_, state_listener_, name_, known_features_)
|
|
555
729
|
: io::mcbp_session(client_id_, {}, ctx_, origin_, state_listener_, name_, known_features_);
|
|
730
|
+
{
|
|
731
|
+
// Publish the in-flight session so close() can stop it if the cluster is torn down before
|
|
732
|
+
// this bootstrap completes; otherwise the continuation below (which captures new_session)
|
|
733
|
+
// would keep it alive forever. See bootstrapping_session_.
|
|
734
|
+
const std::scoped_lock lock(sessions_mutex_);
|
|
735
|
+
if (closed_) {
|
|
736
|
+
// close() ran concurrently after the entry check above (it sets closed_ before it swaps and
|
|
737
|
+
// stops sessions_ under sessions_mutex_). Do not publish a session close() has already
|
|
738
|
+
// swept past -- nothing would ever stop it. The bootstrap continuation is not registered
|
|
739
|
+
// yet, so returning here strands nothing.
|
|
740
|
+
return handler(errc::common::request_canceled, {});
|
|
741
|
+
}
|
|
742
|
+
bootstrapping_session_ = new_session;
|
|
743
|
+
}
|
|
556
744
|
new_session.bootstrap([self = shared_from_this(), new_session, h = std::move(handler)](
|
|
557
745
|
std::error_code ec, topology::configuration cfg) mutable {
|
|
558
746
|
if (ec) {
|
|
559
747
|
CB_LOG_WARNING(R"({} failed to bootstrap session ec={}, bucket="{}")",
|
|
560
748
|
new_session.log_prefix(),
|
|
561
749
|
ec.message(),
|
|
562
|
-
self->name_);
|
|
750
|
+
logger::metadata(self->name_));
|
|
563
751
|
self->remove_session(new_session.id());
|
|
564
|
-
|
|
752
|
+
{
|
|
753
|
+
const std::scoped_lock lock(self->sessions_mutex_);
|
|
754
|
+
self->bootstrapping_session_.reset();
|
|
755
|
+
}
|
|
756
|
+
// Deliver the failure inline rather than through the asio::post below: on cluster teardown
|
|
757
|
+
// the io_context is stopped and any queued post is discarded, so a posted completion would
|
|
758
|
+
// be dropped -- a std::future caller then observes broken_promise and a callback caller
|
|
759
|
+
// hangs. On the error path the handler only reports the failure to the operation (it does
|
|
760
|
+
// not re-enter the bucket under a held lock here), so completing it synchronously is safe.
|
|
761
|
+
return h(ec, cfg);
|
|
762
|
+
}
|
|
763
|
+
{
|
|
565
764
|
const std::size_t this_index = new_session.index();
|
|
566
|
-
|
|
567
|
-
new_session.on_stop([id = new_session.id(), self]() {
|
|
568
|
-
self->remove_session(id);
|
|
569
|
-
});
|
|
570
|
-
|
|
765
|
+
bool established{ false };
|
|
571
766
|
{
|
|
767
|
+
// Establish the session and stop tracking it as bootstrapping in one critical section, so
|
|
768
|
+
// a concurrent close() finds it in exactly one of the two containers (never neither).
|
|
572
769
|
const std::scoped_lock lock(self->sessions_mutex_);
|
|
573
|
-
self->
|
|
770
|
+
self->bootstrapping_session_.reset();
|
|
771
|
+
if (self->closed_) {
|
|
772
|
+
// The bucket was closed while this session was still bootstrapping (close() sets
|
|
773
|
+
// closed_ before it swaps the session maps). Do not register callbacks or add it to
|
|
774
|
+
// sessions_: a session established after close() would be stranded, and its on_stop
|
|
775
|
+
// handler could post restart_sessions() during shutdown. Stop it here instead -- no
|
|
776
|
+
// on_stop is registered, so stopping under the lock cannot re-enter remove_session().
|
|
777
|
+
new_session.stop(retry_reason::do_not_retry);
|
|
778
|
+
} else {
|
|
779
|
+
new_session.on_configuration_update(self);
|
|
780
|
+
new_session.on_stop([id = new_session.id(), self]() {
|
|
781
|
+
self->remove_session(id);
|
|
782
|
+
});
|
|
783
|
+
self->sessions_.insert_or_assign(this_index, std::move(new_session));
|
|
784
|
+
established = true;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (established) {
|
|
788
|
+
self->update_config(cfg);
|
|
789
|
+
self->drain_deferred_queue({});
|
|
790
|
+
self->poll_config({});
|
|
574
791
|
}
|
|
575
|
-
self->update_config(cfg);
|
|
576
|
-
self->drain_deferred_queue({});
|
|
577
|
-
self->poll_config({});
|
|
578
792
|
}
|
|
579
793
|
asio::post(
|
|
580
794
|
asio::bind_executor(self->ctx_, [h = std::move(h), ec, cfg = std::move(cfg)]() mutable {
|
|
@@ -601,8 +815,12 @@ public:
|
|
|
601
815
|
}
|
|
602
816
|
return handler(errc::network::configuration_not_available, nullptr);
|
|
603
817
|
}
|
|
604
|
-
|
|
605
|
-
|
|
818
|
+
// Park the continuation until the bucket is configured. defer_command re-checks closed_ under
|
|
819
|
+
// deferred_commands_mutex_, so a continuation that races close() is either drained or completed
|
|
820
|
+
// with request_canceled instead of being stranded. Stranding would leak the whole cluster
|
|
821
|
+
// graph: the continuation strongly captures the cluster_impl (core), which owns this bucket,
|
|
822
|
+
// which owns the deferred queue that would hold the orphaned continuation (CXXCBC-846).
|
|
823
|
+
defer_command(
|
|
606
824
|
[self = shared_from_this(), handler = std::move(handler)](std::error_code ec) mutable {
|
|
607
825
|
if (ec == errc::common::request_canceled || self->closed_ || !self->configured_) {
|
|
608
826
|
return handler(errc::network::configuration_not_available, nullptr);
|
|
@@ -704,6 +922,14 @@ public:
|
|
|
704
922
|
|
|
705
923
|
drain_deferred_queue(errc::common::request_canceled);
|
|
706
924
|
|
|
925
|
+
// A request waiting for a retry backoff is only ever re-dispatched by its
|
|
926
|
+
// timer, which a closed bucket never fires. Complete them here, for the same
|
|
927
|
+
// reason the deferred queue is drained above.
|
|
928
|
+
retry_backoff_queue_.close();
|
|
929
|
+
retry_backoff_queue_.drain([](const auto& request) {
|
|
930
|
+
request->try_callback({}, errc::common::request_canceled);
|
|
931
|
+
});
|
|
932
|
+
|
|
707
933
|
if (state_listener_ != nullptr) {
|
|
708
934
|
state_listener_->unregister_config_listener(shared_from_this());
|
|
709
935
|
}
|
|
@@ -714,13 +940,20 @@ public:
|
|
|
714
940
|
}
|
|
715
941
|
|
|
716
942
|
std::map<size_t, io::mcbp_session> old_sessions;
|
|
943
|
+
std::optional<io::mcbp_session> bootstrapping_session;
|
|
717
944
|
{
|
|
718
945
|
const std::scoped_lock lock(sessions_mutex_);
|
|
719
946
|
std::swap(old_sessions, sessions_);
|
|
947
|
+
std::swap(bootstrapping_session, bootstrapping_session_);
|
|
720
948
|
}
|
|
721
949
|
for (auto& [index, session] : old_sessions) {
|
|
722
950
|
session.stop(retry_reason::do_not_retry);
|
|
723
951
|
}
|
|
952
|
+
// Stop the session that is still bootstrapping (if any): this completes its bootstrap with an
|
|
953
|
+
// error, releasing the continuation that would otherwise strand the session and this bucket.
|
|
954
|
+
if (bootstrapping_session) {
|
|
955
|
+
bootstrapping_session->stop(retry_reason::do_not_retry);
|
|
956
|
+
}
|
|
724
957
|
}
|
|
725
958
|
|
|
726
959
|
/**
|
|
@@ -831,8 +1064,19 @@ public:
|
|
|
831
1064
|
}
|
|
832
1065
|
}
|
|
833
1066
|
}
|
|
1067
|
+
std::vector<io::mcbp_session> dropped_sessions{};
|
|
834
1068
|
if (!added.empty() || !removed.empty() || sequence_changed) {
|
|
835
1069
|
const std::scoped_lock lock(sessions_mutex_);
|
|
1070
|
+
if (closed_) {
|
|
1071
|
+
// The bucket was closed concurrently (close() sets closed_ before it swaps and stops
|
|
1072
|
+
// sessions_ under sessions_mutex_). Do not rebuild the session list: any session created
|
|
1073
|
+
// here would be established after close() has already torn everything down. Because each
|
|
1074
|
+
// new session's bootstrap continuation strongly captures the session and this bucket (via
|
|
1075
|
+
// on_configuration_update()/on_stop()), and nothing stops a session added after close(),
|
|
1076
|
+
// it would strand the whole bucket/session graph as a pure cycle that LeakSanitizer reports
|
|
1077
|
+
// as leaked. bootstrap() guards the initial session the same way (see closed_ there).
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
836
1080
|
std::map<size_t, io::mcbp_session> new_sessions{};
|
|
837
1081
|
|
|
838
1082
|
std::size_t next_index{ 0 };
|
|
@@ -848,12 +1092,11 @@ public:
|
|
|
848
1092
|
for (auto it = sessions_.begin(); it != sessions_.end(); ++it) {
|
|
849
1093
|
if (it->second.bootstrap_hostname() == hostname &&
|
|
850
1094
|
it->second.bootstrap_port_number() == port) {
|
|
851
|
-
CB_LOG_DEBUG(R"({} rev={}, preserve session="{}", address="{}
|
|
1095
|
+
CB_LOG_DEBUG(R"({} rev={}, preserve session="{}", address="{}", index={}->{})",
|
|
852
1096
|
log_prefix_,
|
|
853
1097
|
config.rev_str(),
|
|
854
1098
|
it->second.id(),
|
|
855
|
-
it->second.
|
|
856
|
-
it->second.bootstrap_port(),
|
|
1099
|
+
logger::system_data(it->second.bootstrap_address()),
|
|
857
1100
|
it->first,
|
|
858
1101
|
next_index);
|
|
859
1102
|
new_sessions.insert_or_assign(next_index, std::move(it->second));
|
|
@@ -885,23 +1128,21 @@ public:
|
|
|
885
1128
|
known_features_)
|
|
886
1129
|
: io::mcbp_session(
|
|
887
1130
|
client_id_, node.node_uuid, ctx_, origin, state_listener_, name_, known_features_);
|
|
888
|
-
CB_LOG_DEBUG(R"({} rev={}, add session="{}", address="{}
|
|
1131
|
+
CB_LOG_DEBUG(R"({} rev={}, add session="{}", address="{}", index={})",
|
|
889
1132
|
log_prefix_,
|
|
890
1133
|
config.rev_str(),
|
|
891
1134
|
session.id(),
|
|
892
|
-
hostname,
|
|
893
|
-
port,
|
|
1135
|
+
logger::system_data(fmt::format("{}:{}", hostname, port)),
|
|
894
1136
|
node.index);
|
|
895
1137
|
session.bootstrap(
|
|
896
1138
|
[self = shared_from_this(), session, idx = next_index](
|
|
897
1139
|
std::error_code err, topology::configuration cfg) mutable {
|
|
898
1140
|
if (err) {
|
|
899
1141
|
CB_LOG_WARNING(
|
|
900
|
-
R"({} failed to bootstrap session="{}", address="{}
|
|
1142
|
+
R"({} failed to bootstrap session="{}", address="{}", index={}, ec={})",
|
|
901
1143
|
session.log_prefix(),
|
|
902
1144
|
session.id(),
|
|
903
|
-
session.
|
|
904
|
-
session.bootstrap_port(),
|
|
1145
|
+
logger::system_data(session.bootstrap_address()),
|
|
905
1146
|
idx,
|
|
906
1147
|
err.message());
|
|
907
1148
|
return self->remove_session(session.id());
|
|
@@ -920,18 +1161,24 @@ public:
|
|
|
920
1161
|
std::swap(sessions_, new_sessions);
|
|
921
1162
|
|
|
922
1163
|
for (auto it = new_sessions.begin(); it != new_sessions.end(); ++it) {
|
|
923
|
-
CB_LOG_DEBUG(R"({} rev={}, drop session="{}", address="{}
|
|
1164
|
+
CB_LOG_DEBUG(R"({} rev={}, drop session="{}", address="{}", index={})",
|
|
924
1165
|
log_prefix_,
|
|
925
1166
|
config.rev_str(),
|
|
926
1167
|
it->second.id(),
|
|
927
|
-
it->second.
|
|
928
|
-
it->second.bootstrap_port(),
|
|
1168
|
+
logger::system_data(it->second.bootstrap_address()),
|
|
929
1169
|
it->first);
|
|
930
|
-
|
|
931
|
-
return session.stop(retry_reason::do_not_retry);
|
|
932
|
-
}));
|
|
1170
|
+
dropped_sessions.push_back(std::move(it->second));
|
|
933
1171
|
}
|
|
934
1172
|
}
|
|
1173
|
+
// Stop dropped sessions synchronously, after releasing sessions_mutex_ (stop() fires on_stop ->
|
|
1174
|
+
// remove_session(), which re-acquires the mutex). Stopping synchronously -- rather than via
|
|
1175
|
+
// asio::post -- clears each session's bucket-capturing handlers (config_listeners_,
|
|
1176
|
+
// on_stop_handler_) right now. A posted stop can be abandoned by io_context shutdown before it
|
|
1177
|
+
// runs, leaving those handlers set and stranding the whole bucket/session graph as a pure cycle
|
|
1178
|
+
// (LeakSanitizer reports it as all-indirect leaks).
|
|
1179
|
+
for (auto& session : dropped_sessions) {
|
|
1180
|
+
session.stop(retry_reason::do_not_retry);
|
|
1181
|
+
}
|
|
935
1182
|
}
|
|
936
1183
|
|
|
937
1184
|
[[nodiscard]] auto find_session_by_index(std::size_t index) const
|
|
@@ -944,14 +1191,15 @@ public:
|
|
|
944
1191
|
return {};
|
|
945
1192
|
}
|
|
946
1193
|
|
|
947
|
-
[[nodiscard]] auto find_or_connect_session_by_index(std::size_t index)
|
|
948
|
-
-> std::optional<io::mcbp_session>
|
|
1194
|
+
[[nodiscard]] auto find_or_connect_session_by_index(std::size_t index) -> routed_session
|
|
949
1195
|
{
|
|
950
1196
|
if (auto session = find_session_by_index(index); session) {
|
|
951
|
-
|
|
1197
|
+
// A session exists, so it is safe to defer behind it. That holds even
|
|
1198
|
+
// if it is still bootstrapping: the completion that finishes it is what
|
|
1199
|
+
// drains the deferred queue.
|
|
1200
|
+
return { session, true };
|
|
952
1201
|
}
|
|
953
|
-
connect_session(index);
|
|
954
|
-
return {};
|
|
1202
|
+
return { {}, connect_session(index) };
|
|
955
1203
|
}
|
|
956
1204
|
|
|
957
1205
|
[[nodiscard]] auto next_session_index() -> std::size_t
|
|
@@ -1048,9 +1296,18 @@ public:
|
|
|
1048
1296
|
|
|
1049
1297
|
auto defer_command(utils::movable_function<void(std::error_code)> command) -> std::error_code
|
|
1050
1298
|
{
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1299
|
+
{
|
|
1300
|
+
const std::scoped_lock lock_for_deferred_commands(deferred_commands_mutex_);
|
|
1301
|
+
if (!closed_) {
|
|
1302
|
+
deferred_commands_.emplace(std::move(command));
|
|
1303
|
+
return {};
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
// The bucket was closed (and its deferred queue already drained) before this command could be
|
|
1307
|
+
// parked. Enqueuing now would strand the command forever, leaking whatever its continuation
|
|
1308
|
+
// captures. Complete it with a cancellation instead so its state is released (CXXCBC-846).
|
|
1309
|
+
command(errc::common::request_canceled);
|
|
1310
|
+
return errc::common::request_canceled;
|
|
1054
1311
|
}
|
|
1055
1312
|
|
|
1056
1313
|
void for_each_session(utils::movable_function<void(io::mcbp_session&)> handler)
|
|
@@ -1097,7 +1354,17 @@ private:
|
|
|
1097
1354
|
std::queue<utils::movable_function<void(std::error_code)>> deferred_commands_{};
|
|
1098
1355
|
std::mutex deferred_commands_mutex_{};
|
|
1099
1356
|
|
|
1357
|
+
// Requests waiting for a retry backoff, so that close() can complete them.
|
|
1358
|
+
mcbp::operation_queue retry_backoff_queue_{};
|
|
1359
|
+
|
|
1100
1360
|
std::map<size_t, io::mcbp_session> sessions_{};
|
|
1361
|
+
// The very first session is only moved into sessions_ once it finishes bootstrapping; until then
|
|
1362
|
+
// it lives solely inside its own bootstrap continuation (which captures a copy of the session and
|
|
1363
|
+
// this bucket). If the cluster is closed while that bootstrap is in flight (e.g. a bucket that
|
|
1364
|
+
// never opens), close() would otherwise never see the session, so its self-capture would strand
|
|
1365
|
+
// the session, the bucket, and the whole cluster graph. Track it here so close() can stop it and
|
|
1366
|
+
// let the continuation fire. Guarded by sessions_mutex_.
|
|
1367
|
+
std::optional<io::mcbp_session> bootstrapping_session_{};
|
|
1101
1368
|
mutable std::mutex sessions_mutex_{};
|
|
1102
1369
|
std::atomic_size_t round_robin_next_{ 0 };
|
|
1103
1370
|
};
|
|
@@ -1287,6 +1554,33 @@ bucket::config_rev() const -> std::string
|
|
|
1287
1554
|
return impl_->config_rev();
|
|
1288
1555
|
}
|
|
1289
1556
|
|
|
1557
|
+
auto
|
|
1558
|
+
bucket::config_snapshot() const -> std::shared_ptr<topology::configuration>
|
|
1559
|
+
{
|
|
1560
|
+
return impl_->config_snapshot();
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
bucket_unit_test_api::bucket_unit_test_api(std::shared_ptr<bucket_impl> impl)
|
|
1564
|
+
: impl_{ std::move(impl) }
|
|
1565
|
+
{
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
void
|
|
1569
|
+
bucket_unit_test_api::resolve_response(const std::shared_ptr<mcbp::queue_request>& request,
|
|
1570
|
+
const std::shared_ptr<mcbp::queue_response>& response,
|
|
1571
|
+
std::error_code ec,
|
|
1572
|
+
retry_reason reason,
|
|
1573
|
+
std::optional<key_value_error_map_info> error_info)
|
|
1574
|
+
{
|
|
1575
|
+
impl_->resolve_response(request, response, ec, reason, std::move(error_info));
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
auto
|
|
1579
|
+
bucket::unit_test_api() -> bucket_unit_test_api
|
|
1580
|
+
{
|
|
1581
|
+
return bucket_unit_test_api(impl_);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1290
1584
|
auto
|
|
1291
1585
|
bucket::direct_dispatch(std::shared_ptr<mcbp::queue_request> req) -> std::error_code
|
|
1292
1586
|
{
|
|
@@ -1300,8 +1594,8 @@ bucket::direct_re_queue(const std::shared_ptr<mcbp::queue_request>& req, bool is
|
|
|
1300
1594
|
return impl_->direct_re_queue(req, is_retry);
|
|
1301
1595
|
}
|
|
1302
1596
|
|
|
1303
|
-
|
|
1304
|
-
bucket::connect_session(std::size_t index)
|
|
1597
|
+
auto
|
|
1598
|
+
bucket::connect_session(std::size_t index) -> bool
|
|
1305
1599
|
{
|
|
1306
1600
|
return impl_->connect_session(index);
|
|
1307
1601
|
}
|