couchbase 3.8.2 → 3.9.1
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 +136 -23
- 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 +82 -7
- 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 +1551 -397
- 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 +60 -6
- 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 +13 -37
- 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 +33 -1
- 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
|
@@ -17,20 +17,17 @@
|
|
|
17
17
|
|
|
18
18
|
#pragma once
|
|
19
19
|
|
|
20
|
-
#include <couchbase/build_config.hxx>
|
|
21
|
-
|
|
22
20
|
#include "core/app_telemetry_meter.hxx"
|
|
23
21
|
#include "core/config_listener.hxx"
|
|
24
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
25
|
-
#include "core/columnar/bootstrap_notification_subscriber.hxx"
|
|
26
|
-
#endif
|
|
27
22
|
#include "core/logger/logger.hxx"
|
|
23
|
+
#include "core/logger/redaction.hxx"
|
|
28
24
|
#include "core/metrics/meter_wrapper.hxx"
|
|
29
25
|
#include "core/operations/http_noop.hxx"
|
|
30
26
|
#include "core/service_type.hxx"
|
|
31
27
|
#include "core/tls_context_provider.hxx"
|
|
32
28
|
#include "core/tracing/noop_tracer.hxx"
|
|
33
29
|
#include "core/tracing/tracer_wrapper.hxx"
|
|
30
|
+
#include "core/utils/movable_function.hxx"
|
|
34
31
|
#include "http_command.hxx"
|
|
35
32
|
#include "http_context.hxx"
|
|
36
33
|
#include "http_session.hxx"
|
|
@@ -38,26 +35,22 @@
|
|
|
38
35
|
|
|
39
36
|
#include <gsl/narrow>
|
|
40
37
|
|
|
38
|
+
#include <atomic>
|
|
41
39
|
#include <chrono>
|
|
40
|
+
#include <exception>
|
|
42
41
|
#include <optional>
|
|
43
42
|
#include <queue>
|
|
44
43
|
#include <random>
|
|
44
|
+
#include <utility>
|
|
45
|
+
#include <vector>
|
|
45
46
|
|
|
46
47
|
namespace couchbase::core::io
|
|
47
48
|
{
|
|
48
49
|
|
|
49
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
50
|
-
class http_session_manager
|
|
51
|
-
: public std::enable_shared_from_this<http_session_manager>
|
|
52
|
-
, public config_listener
|
|
53
|
-
, public columnar::bootstrap_notification_subscriber
|
|
54
|
-
{
|
|
55
|
-
#else
|
|
56
50
|
class http_session_manager
|
|
57
51
|
: public std::enable_shared_from_this<http_session_manager>
|
|
58
52
|
, public config_listener
|
|
59
53
|
{
|
|
60
|
-
#endif
|
|
61
54
|
public:
|
|
62
55
|
http_session_manager(std::string client_id,
|
|
63
56
|
asio::io_context& ctx,
|
|
@@ -75,6 +68,11 @@ public:
|
|
|
75
68
|
tracer_ = std::move(tracer);
|
|
76
69
|
}
|
|
77
70
|
|
|
71
|
+
[[nodiscard]] auto tracer() const -> std::shared_ptr<tracing::tracer_wrapper>
|
|
72
|
+
{
|
|
73
|
+
return tracer_;
|
|
74
|
+
}
|
|
75
|
+
|
|
78
76
|
void set_meter(std::shared_ptr<metrics::meter_wrapper> meter)
|
|
79
77
|
{
|
|
80
78
|
meter_ = std::move(meter);
|
|
@@ -91,33 +89,11 @@ public:
|
|
|
91
89
|
return config_.capabilities;
|
|
92
90
|
}
|
|
93
91
|
|
|
94
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
95
|
-
void notify_bootstrap_error(const impl::bootstrap_error& error) override
|
|
96
|
-
{
|
|
97
|
-
CB_LOG_DEBUG("Received bootstrap error notification. code={}, ec_message={}, message={}, "
|
|
98
|
-
"allow_fast_fail={}.",
|
|
99
|
-
error.ec.value(),
|
|
100
|
-
error.ec.message(),
|
|
101
|
-
error.error_message,
|
|
102
|
-
allow_fast_fail_ ? "true" : "false");
|
|
103
|
-
if (allow_fast_fail_) {
|
|
104
|
-
std::scoped_lock bootstrap_error_lock(last_bootstrap_error_mutex_);
|
|
105
|
-
last_bootstrap_error_ = error;
|
|
106
|
-
drain_deferred_queue(last_bootstrap_error_.value());
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
void notify_bootstrap_success(const std::string& session_id) override
|
|
111
|
-
{
|
|
112
|
-
CB_LOG_DEBUG("Received successful bootstrap notification. Session={}.", session_id);
|
|
113
|
-
std::scoped_lock bootstrap_error_lock(last_bootstrap_error_mutex_);
|
|
114
|
-
allow_fast_fail_ = false;
|
|
115
|
-
last_bootstrap_error_.reset();
|
|
116
|
-
}
|
|
117
|
-
#endif
|
|
118
|
-
|
|
119
92
|
void update_config(topology::configuration config) override
|
|
120
93
|
{
|
|
94
|
+
// Idle sessions to nodes that have left the cluster, collected under the lock
|
|
95
|
+
// and stopped afterwards (stop() re-enters sessions_mutex_ via on_stop).
|
|
96
|
+
std::vector<std::shared_ptr<http_session>> evicted;
|
|
121
97
|
{
|
|
122
98
|
std::scoped_lock config_lock(config_mutex_, sessions_mutex_, next_index_mutex_);
|
|
123
99
|
config_ = std::move(config);
|
|
@@ -125,26 +101,28 @@ public:
|
|
|
125
101
|
next_index_ = 0;
|
|
126
102
|
}
|
|
127
103
|
for (auto& [type, sessions] : idle_sessions_) {
|
|
128
|
-
sessions.remove_if([&opts = options_, &cfg = config_](const auto& session) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
104
|
+
sessions.remove_if([&opts = options_, &cfg = config_, &evicted](const auto& session) {
|
|
105
|
+
if (session && !cfg.has_node(opts.network,
|
|
106
|
+
session->type(),
|
|
107
|
+
opts.enable_tls,
|
|
108
|
+
session->hostname(),
|
|
109
|
+
session->port())) {
|
|
110
|
+
evicted.push_back(session);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
134
114
|
});
|
|
135
115
|
}
|
|
136
116
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
dispatch_timeout_ = timeout;
|
|
117
|
+
// Tear the evicted connections down now instead of leaving them open until
|
|
118
|
+
// the idle timer fires; the node is gone, so the socket is dead weight (and,
|
|
119
|
+
// against a proxy that has already dropped the far side, a stale handle).
|
|
120
|
+
for (const auto& session : evicted) {
|
|
121
|
+
asio::post(session->get_executor(), [session]() {
|
|
122
|
+
session->stop();
|
|
123
|
+
});
|
|
124
|
+
}
|
|
146
125
|
}
|
|
147
|
-
#endif
|
|
148
126
|
|
|
149
127
|
void set_configuration(const topology::configuration& config, const cluster_options& options)
|
|
150
128
|
{
|
|
@@ -160,18 +138,7 @@ public:
|
|
|
160
138
|
options_ = options;
|
|
161
139
|
next_index_ = next_index;
|
|
162
140
|
config_ = config;
|
|
163
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
164
|
-
if (!configured_) {
|
|
165
|
-
configured_ = true;
|
|
166
|
-
}
|
|
167
|
-
if (allow_fast_fail_) {
|
|
168
|
-
allow_fast_fail_ = false;
|
|
169
|
-
}
|
|
170
|
-
#endif
|
|
171
141
|
}
|
|
172
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
173
|
-
drain_deferred_queue({});
|
|
174
|
-
#endif
|
|
175
142
|
}
|
|
176
143
|
|
|
177
144
|
void export_diag_info(diag::diagnostics_result& res)
|
|
@@ -218,31 +185,26 @@ public:
|
|
|
218
185
|
const auto& hostname = node.hostname_for(options_.network);
|
|
219
186
|
const auto& canonical_hostname = node.hostname;
|
|
220
187
|
std::uint16_t canonical_port = node.port_or(type, options_.enable_tls, 0);
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
188
|
+
// Created and listed pending in one section, as check_out does, so a close() either
|
|
189
|
+
// stops it or precedes it. Unlisted while its connect is pending, it would be invisible
|
|
190
|
+
// to a close() whose completion lets the io_context stop.
|
|
191
|
+
std::shared_ptr<http_session> session;
|
|
192
|
+
{
|
|
193
|
+
const std::scoped_lock lock(sessions_mutex_);
|
|
194
|
+
session = create_session(type,
|
|
195
|
+
node_details{
|
|
196
|
+
hostname,
|
|
197
|
+
port,
|
|
198
|
+
node.node_uuid,
|
|
199
|
+
canonical_hostname,
|
|
200
|
+
canonical_port,
|
|
201
|
+
},
|
|
202
|
+
generation_);
|
|
203
|
+
pending_sessions_[type].push_back(session);
|
|
232
204
|
}
|
|
233
205
|
operations::http_noop_request request{};
|
|
234
206
|
request.type = type;
|
|
235
207
|
request.timeout = timeout;
|
|
236
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
237
|
-
auto cmd = std::make_shared<operations::http_command<operations::http_noop_request>>(
|
|
238
|
-
ctx_,
|
|
239
|
-
request,
|
|
240
|
-
tracer_,
|
|
241
|
-
meter_,
|
|
242
|
-
app_telemetry_meter_,
|
|
243
|
-
options_.default_timeout_for(request.type),
|
|
244
|
-
dispatch_timeout_);
|
|
245
|
-
#else
|
|
246
208
|
auto cmd = std::make_shared<operations::http_command<operations::http_noop_request>>(
|
|
247
209
|
ctx_,
|
|
248
210
|
request,
|
|
@@ -250,12 +212,13 @@ public:
|
|
|
250
212
|
meter_,
|
|
251
213
|
app_telemetry_meter_,
|
|
252
214
|
options_.default_timeout_for(request.type));
|
|
253
|
-
|
|
215
|
+
cmd->set_command_session(session);
|
|
254
216
|
cmd->start(
|
|
255
217
|
[start = std::chrono::steady_clock::now(),
|
|
256
218
|
self = shared_from_this(),
|
|
257
219
|
type,
|
|
258
|
-
|
|
220
|
+
session,
|
|
221
|
+
weak_cmd = std::weak_ptr(cmd),
|
|
259
222
|
handler = collector->build_reporter()](operations::http_noop_response&& resp) {
|
|
260
223
|
diag::ping_state state = diag::ping_state::ok;
|
|
261
224
|
std::optional<std::string> error{};
|
|
@@ -271,27 +234,27 @@ public:
|
|
|
271
234
|
ec.message(),
|
|
272
235
|
resp.ctx.http_status));
|
|
273
236
|
}
|
|
274
|
-
auto remote_address =
|
|
237
|
+
auto remote_address = session->remote_address();
|
|
275
238
|
// If not connected, the remote address will be empty. Better to
|
|
276
239
|
// give the user some context on the "attempted" remote address.
|
|
277
240
|
if (remote_address.empty()) {
|
|
278
|
-
remote_address =
|
|
279
|
-
fmt::format("{}:{}", cmd->session_->hostname(), cmd->session_->port());
|
|
241
|
+
remote_address = fmt::format("{}:{}", session->hostname(), session->port());
|
|
280
242
|
}
|
|
281
243
|
handler->report(
|
|
282
244
|
diag::endpoint_ping_info{ type,
|
|
283
|
-
|
|
245
|
+
session->id(),
|
|
284
246
|
std::chrono::duration_cast<std::chrono::microseconds>(
|
|
285
247
|
std::chrono::steady_clock::now() - start),
|
|
286
248
|
remote_address,
|
|
287
|
-
|
|
249
|
+
session->local_address(),
|
|
288
250
|
state,
|
|
289
251
|
{},
|
|
290
252
|
error });
|
|
291
|
-
|
|
253
|
+
if (const auto cmd = weak_cmd.lock(); cmd) {
|
|
254
|
+
self->check_in(type, cmd->session_for_check_in());
|
|
255
|
+
}
|
|
292
256
|
});
|
|
293
257
|
|
|
294
|
-
cmd->set_command_session(session);
|
|
295
258
|
if (!session->is_connected()) {
|
|
296
259
|
connect_then_send(session, cmd, {}, true);
|
|
297
260
|
} else {
|
|
@@ -318,9 +281,8 @@ public:
|
|
|
318
281
|
node_details preferred_node{};
|
|
319
282
|
|
|
320
283
|
if (preferred_node_address.empty() && !undesired_node_address.empty()) {
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
// selected node (as opposed to any node we have an idle session to).
|
|
284
|
+
// No sticky node, but one to avoid. Retrying elsewhere requires a different node chosen
|
|
285
|
+
// at random, not merely any node this manager happens to hold an idle session to.
|
|
324
286
|
if (auto n = pick_random_node(type, undesired_node_address); n.port != 0) {
|
|
325
287
|
preferred_node_address = fmt::format("{}:{}", n.hostname, n.port);
|
|
326
288
|
preferred_node = std::move(n);
|
|
@@ -344,41 +306,26 @@ public:
|
|
|
344
306
|
return !s;
|
|
345
307
|
});
|
|
346
308
|
std::shared_ptr<http_session> session{};
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
auto
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
idle_sessions_[type].erase(ptr);
|
|
368
|
-
if (session->reset_idle()) {
|
|
369
|
-
break;
|
|
370
|
-
}
|
|
371
|
-
} else {
|
|
372
|
-
session = create_session(type, preferred_node);
|
|
373
|
-
break;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
CB_LOG_TRACE(
|
|
377
|
-
"{} Idle timer has expired for \"{}:{}\". Attempting to select another session.",
|
|
378
|
-
session->log_prefix(),
|
|
379
|
-
session->hostname(),
|
|
380
|
-
session->port());
|
|
381
|
-
session.reset();
|
|
309
|
+
// Only a reusable session leaves the idle list. One that is not has a stop queued, by its idle
|
|
310
|
+
// timer or by retire_sessions(), and stays listed until on_stop unlists it, so a close() before
|
|
311
|
+
// then still stops it and waits for it.
|
|
312
|
+
auto& idle = idle_sessions_[type];
|
|
313
|
+
const auto taken = std::find_if(
|
|
314
|
+
idle.begin(),
|
|
315
|
+
idle.end(),
|
|
316
|
+
[&preferred_node_address, &h = preferred_node.hostname, &p = preferred_node.port](
|
|
317
|
+
const auto& s) {
|
|
318
|
+
// Check for a match using both the unresolved hostname & IP address
|
|
319
|
+
const bool matches = preferred_node_address.empty() ||
|
|
320
|
+
s->remote_address() == preferred_node_address ||
|
|
321
|
+
(s->hostname() == h && s->port() == std::to_string(p));
|
|
322
|
+
return matches && is_reusable(*s);
|
|
323
|
+
});
|
|
324
|
+
if (taken != idle.end()) {
|
|
325
|
+
session = *taken;
|
|
326
|
+
idle.erase(taken);
|
|
327
|
+
} else if (!preferred_node_address.empty()) {
|
|
328
|
+
session = create_session(type, preferred_node, generation_);
|
|
382
329
|
}
|
|
383
330
|
if (!session) {
|
|
384
331
|
auto node = preferred_node_address.empty() ? next_node(type)
|
|
@@ -386,7 +333,7 @@ public:
|
|
|
386
333
|
if (node.port == 0) {
|
|
387
334
|
return { errc::common::service_not_available, nullptr };
|
|
388
335
|
}
|
|
389
|
-
session = create_session(type, node);
|
|
336
|
+
session = create_session(type, node, generation_);
|
|
390
337
|
}
|
|
391
338
|
if (session->is_connected()) {
|
|
392
339
|
busy_sessions_[type].push_back(session);
|
|
@@ -415,17 +362,48 @@ public:
|
|
|
415
362
|
return;
|
|
416
363
|
}
|
|
417
364
|
bool should_stop = false;
|
|
418
|
-
std::chrono::milliseconds idle_timeout{};
|
|
419
365
|
{
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
366
|
+
// config_mutex_ guards config_, which set_configuration() writes without sessions_mutex_.
|
|
367
|
+
// sessions_mutex_ in the same section keeps update_config()'s eviction, which holds both,
|
|
368
|
+
// from landing between the has_node test and the publication: that would pool a connection
|
|
369
|
+
// to a node that has left the cluster. check_in must therefore never be called with
|
|
370
|
+
// sessions_mutex_ held.
|
|
371
|
+
const std::scoped_lock lock(config_mutex_, sessions_mutex_);
|
|
372
|
+
// stopped_ is set before stop()'s teardown runs on_stop, whose cleanup takes
|
|
373
|
+
// sessions_mutex_. Read under that lock, either the stop is seen here, or its cleanup runs
|
|
374
|
+
// after the publication below and removes the session again.
|
|
375
|
+
//
|
|
376
|
+
// stopping_ is a stop the session's owner has decided and not yet executed, claimed before
|
|
377
|
+
// this check_in can run: by a streaming body under its own mutex while reading_complete_ is
|
|
378
|
+
// false (http_session::read_some runs the body's completion, which sets it, before the
|
|
379
|
+
// stream-end handler).
|
|
380
|
+
if (session->is_stopped() || session->is_stopping()) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
if (session->pool_generation() != generation_ || session->tls_epoch() != tls_epoch_ ||
|
|
384
|
+
!session->keep_alive() ||
|
|
385
|
+
!config_.has_node(options_.network,
|
|
386
|
+
session->type(),
|
|
387
|
+
options_.enable_tls,
|
|
388
|
+
session->hostname(),
|
|
389
|
+
session->port())) {
|
|
426
390
|
should_stop = true;
|
|
427
391
|
} else {
|
|
428
|
-
|
|
392
|
+
// Under sessions_mutex_, as check_out's reset_idle() is. set_idle() binds the timer
|
|
393
|
+
// completion to the strand and never calls stop() inline, so on_stop cannot re-enter
|
|
394
|
+
// sessions_mutex_ here.
|
|
395
|
+
session->set_idle(options_.idle_http_connection_timeout);
|
|
396
|
+
idle_sessions_[type].push_back(session);
|
|
397
|
+
if (auto busy_it = busy_sessions_.find(type); busy_it != busy_sessions_.end()) {
|
|
398
|
+
busy_it->second.remove_if([id = session->id()](const auto& s) -> bool {
|
|
399
|
+
return !s || s->id() == id;
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
if (auto pend_it = pending_sessions_.find(type); pend_it != pending_sessions_.end()) {
|
|
403
|
+
pend_it->second.remove_if([id = session->id()](const auto& s) -> bool {
|
|
404
|
+
return !s || s->id() == id;
|
|
405
|
+
});
|
|
406
|
+
}
|
|
429
407
|
}
|
|
430
408
|
}
|
|
431
409
|
if (should_stop) {
|
|
@@ -433,72 +411,107 @@ public:
|
|
|
433
411
|
session->stop();
|
|
434
412
|
});
|
|
435
413
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
414
|
+
CB_LOG_DEBUG("{} put HTTP session back to idle connections", session->log_prefix());
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Stops the reuse of every session created before the call, for a credential that the connection
|
|
418
|
+
// carries, such as a client certificate. Idle sessions stop now. A busy or connecting session
|
|
419
|
+
// completes its request first; check_in then stops it, because its TLS epoch is stale.
|
|
420
|
+
void retire_sessions()
|
|
421
|
+
{
|
|
422
|
+
std::vector<std::shared_ptr<http_session>> retired;
|
|
423
|
+
{
|
|
424
|
+
const std::scoped_lock lock(sessions_mutex_);
|
|
425
|
+
++tls_epoch_;
|
|
426
|
+
// Claimed but left listed until their stop runs, so a close() before then stops them and
|
|
427
|
+
// waits for them. check_out skips a claimed session, and on_stop unlists it.
|
|
428
|
+
for (const auto& [type, sessions] : idle_sessions_) {
|
|
429
|
+
for (const auto& s : sessions) {
|
|
430
|
+
if (s) {
|
|
431
|
+
s->mark_stopping();
|
|
432
|
+
retired.push_back(s);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
453
435
|
}
|
|
454
436
|
}
|
|
437
|
+
// Stopped on its own strand, as close() does.
|
|
438
|
+
for (auto& s : retired) {
|
|
439
|
+
asio::post(s->get_executor(), [s]() {
|
|
440
|
+
s->stop();
|
|
441
|
+
});
|
|
442
|
+
}
|
|
455
443
|
}
|
|
456
444
|
|
|
457
|
-
|
|
445
|
+
// on_stopped runs once every session listed at the call has been stopped and holds no
|
|
446
|
+
// read_some() call, on the strand of the last one, or before close() returns when none is listed.
|
|
447
|
+
// Until then the read_some() calls queued on a session hold the body that holds the session, and
|
|
448
|
+
// its on_stop cleanup holds this manager. Whoever stops the io_context must wait for it: a
|
|
449
|
+
// handler still queued then never runs, and the session, its body and this manager are never
|
|
450
|
+
// released. A do_read() the stop aborts is not waited for: its completion holds only the session,
|
|
451
|
+
// which destroying the io_context releases.
|
|
452
|
+
void close(utils::movable_function<void()> on_stopped = {})
|
|
458
453
|
{
|
|
459
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
460
|
-
drain_deferred_queue(errc::common::request_canceled);
|
|
461
|
-
#endif
|
|
462
454
|
std::map<service_type, std::list<std::shared_ptr<http_session>>> busy_sessions, idle_sessions,
|
|
463
455
|
pending_sessions;
|
|
464
456
|
{
|
|
457
|
+
// A new generation starts with the lists moved out. check_in and publish_busy refuse a
|
|
458
|
+
// session from an earlier one, so none is listed again; a connect callback stops its session
|
|
459
|
+
// instead. check_out creates sessions in the new generation.
|
|
465
460
|
const std::scoped_lock lock(sessions_mutex_);
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
461
|
+
++generation_;
|
|
462
|
+
// Exchanged rather than moved: the manager goes on serving, and a moved-from map is only
|
|
463
|
+
// valid, not empty.
|
|
464
|
+
busy_sessions = std::exchange(busy_sessions_, {});
|
|
465
|
+
idle_sessions = std::exchange(idle_sessions_, {});
|
|
466
|
+
pending_sessions = std::exchange(pending_sessions_, {});
|
|
467
|
+
}
|
|
468
|
+
// Every session is stopped on its own strand, as check_in and update_config() do. stop()
|
|
469
|
+
// closes the stream and cancels the response, which the strand's read and write handlers use.
|
|
470
|
+
// Run on this thread, it races those handlers, and a queued do_write() finds no socket. An
|
|
471
|
+
// idle session is stopped rather than only reset_idle()d, so the read armed by set_idle() is
|
|
472
|
+
// torn down and its shared_ptr does not keep the io_context from draining.
|
|
473
|
+
std::vector<std::shared_ptr<http_session>> stopping;
|
|
474
|
+
for (auto* lists : { &idle_sessions, &busy_sessions, &pending_sessions }) {
|
|
475
|
+
for (auto& [type, sessions] : *lists) {
|
|
476
|
+
for (auto& s : sessions) {
|
|
477
|
+
if (s) {
|
|
478
|
+
stopping.emplace_back(std::move(s));
|
|
479
|
+
}
|
|
475
480
|
}
|
|
476
481
|
}
|
|
477
482
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
s->stop();
|
|
482
|
-
}
|
|
483
|
+
if (stopping.empty()) {
|
|
484
|
+
if (on_stopped) {
|
|
485
|
+
on_stopped();
|
|
483
486
|
}
|
|
487
|
+
return;
|
|
484
488
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
489
|
+
auto remaining = std::make_shared<std::atomic_size_t>(stopping.size());
|
|
490
|
+
auto done = std::make_shared<utils::movable_function<void()>>(std::move(on_stopped));
|
|
491
|
+
for (auto& s : stopping) {
|
|
492
|
+
asio::post(s->get_executor(), [s, remaining, done]() {
|
|
493
|
+
// A stop() that throws has still torn the session down, so its reads still drain.
|
|
494
|
+
std::exception_ptr error{};
|
|
495
|
+
try {
|
|
488
496
|
s->stop();
|
|
497
|
+
} catch (...) {
|
|
498
|
+
error = std::current_exception();
|
|
489
499
|
}
|
|
490
|
-
|
|
500
|
+
s->on_reads_drained([remaining, done]() {
|
|
501
|
+
if (remaining->fetch_sub(1) == 1 && *done) {
|
|
502
|
+
(*done)();
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
if (error) {
|
|
506
|
+
std::rethrow_exception(error);
|
|
507
|
+
}
|
|
508
|
+
});
|
|
491
509
|
}
|
|
492
510
|
}
|
|
493
511
|
|
|
494
512
|
template<typename Request, typename Handler>
|
|
495
513
|
void execute(Request request, Handler&& handler)
|
|
496
514
|
{
|
|
497
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
498
|
-
if (!configured_) {
|
|
499
|
-
return defer_command(request, std::move(handler));
|
|
500
|
-
}
|
|
501
|
-
#endif
|
|
502
515
|
std::string preferred_node;
|
|
503
516
|
if constexpr (http_traits::supports_sticky_node_v<Request>) {
|
|
504
517
|
if (request.send_to_node) {
|
|
@@ -513,16 +526,6 @@ public:
|
|
|
513
526
|
return handler(request.make_response(std::move(ctx), encoded_response_type{}));
|
|
514
527
|
}
|
|
515
528
|
|
|
516
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
517
|
-
auto cmd = std::make_shared<operations::http_command<Request>>(
|
|
518
|
-
ctx_,
|
|
519
|
-
request,
|
|
520
|
-
tracer_,
|
|
521
|
-
meter_,
|
|
522
|
-
app_telemetry_meter_,
|
|
523
|
-
options_.default_timeout_for(request.type),
|
|
524
|
-
dispatch_timeout_);
|
|
525
|
-
#else
|
|
526
529
|
auto cmd = std::make_shared<operations::http_command<Request>>(
|
|
527
530
|
ctx_,
|
|
528
531
|
request,
|
|
@@ -530,15 +533,15 @@ public:
|
|
|
530
533
|
meter_,
|
|
531
534
|
app_telemetry_meter_,
|
|
532
535
|
options_.default_timeout_for(request.type));
|
|
533
|
-
#endif
|
|
534
536
|
|
|
535
537
|
using response_type = typename Request::response_type;
|
|
538
|
+
// Before start(), so a deadline that fires at once still reports the session's endpoint.
|
|
539
|
+
cmd->set_command_session(session);
|
|
536
540
|
cmd->start([self = shared_from_this(), cmd, handler = std::forward<Handler>(handler)](
|
|
537
541
|
response_type&& resp) mutable {
|
|
538
542
|
handler(std::move(resp));
|
|
539
|
-
self->check_in(cmd->request.type, cmd->
|
|
543
|
+
self->check_in(cmd->request.type, cmd->session_for_check_in());
|
|
540
544
|
});
|
|
541
|
-
cmd->set_command_session(session);
|
|
542
545
|
if (!session->is_connected()) {
|
|
543
546
|
connect_then_send(session, cmd, preferred_node);
|
|
544
547
|
} else {
|
|
@@ -546,49 +549,34 @@ public:
|
|
|
546
549
|
}
|
|
547
550
|
}
|
|
548
551
|
|
|
549
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
550
552
|
void connect_then_send_pending_op(
|
|
551
553
|
std::shared_ptr<http_session> session,
|
|
552
554
|
const std::string& preferred_node,
|
|
553
|
-
std::chrono::time_point<std::chrono::steady_clock> dispatch_deadline,
|
|
554
555
|
std::chrono::time_point<std::chrono::steady_clock> deadline,
|
|
555
556
|
utils::movable_function<void(std::error_code, std::shared_ptr<http_session>)> callback)
|
|
556
|
-
#else
|
|
557
|
-
void connect_then_send_pending_op(
|
|
558
|
-
std::shared_ptr<http_session> session,
|
|
559
|
-
const std::string& preferred_node,
|
|
560
|
-
std::chrono::time_point<std::chrono::steady_clock> deadline,
|
|
561
|
-
utils::movable_function<void(std::error_code, std::shared_ptr<http_session>)> callback)
|
|
562
|
-
#endif
|
|
563
557
|
{
|
|
564
558
|
session->connect([self = shared_from_this(),
|
|
565
559
|
session,
|
|
566
560
|
preferred_node,
|
|
567
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
568
|
-
dispatch_deadline,
|
|
569
|
-
#endif
|
|
570
561
|
deadline,
|
|
571
562
|
cb = std::move(callback)]() mutable {
|
|
563
|
+
// stop() runs this callback, from close() among others. A stopped session is terminal: a
|
|
564
|
+
// replacement would open a socket for an operation the stop has ended.
|
|
565
|
+
if (session->is_stopped()) {
|
|
566
|
+
return cb(errc::common::request_canceled, {});
|
|
567
|
+
}
|
|
568
|
+
// A session from before a close() gets no replacement and no reconnect: close() may not reach
|
|
569
|
+
// it, so it is stopped here.
|
|
570
|
+
if (self->is_retired(*session)) {
|
|
571
|
+
session->stop();
|
|
572
|
+
return cb(errc::common::request_canceled, {});
|
|
573
|
+
}
|
|
572
574
|
if (!session->is_connected()) {
|
|
573
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
574
|
-
auto now = std::chrono::steady_clock::now();
|
|
575
|
-
if (session->is_stopped()) {
|
|
576
|
-
// Session was forced to stop (e.g. due to cluster being closed or cancellation)
|
|
577
|
-
cb(errc::common::request_canceled, {});
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
if (dispatch_deadline < now || deadline < now) {
|
|
581
|
-
session->stop();
|
|
582
|
-
cb(errc::common::unambiguous_timeout, {});
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
#else
|
|
586
575
|
if (deadline < std::chrono::steady_clock::now()) {
|
|
587
576
|
session->stop();
|
|
588
577
|
return cb(errc::common::unambiguous_timeout, {});
|
|
589
578
|
return;
|
|
590
579
|
}
|
|
591
|
-
#endif
|
|
592
580
|
|
|
593
581
|
// stop this session and create a new one w/ new hostname + port
|
|
594
582
|
session->stop();
|
|
@@ -599,76 +587,34 @@ public:
|
|
|
599
587
|
cb(errc::common::service_not_available, {});
|
|
600
588
|
return;
|
|
601
589
|
}
|
|
602
|
-
auto new_session = self->
|
|
590
|
+
auto new_session = self->create_replacement(session->type(), node, *session);
|
|
591
|
+
if (!new_session) {
|
|
592
|
+
return cb(errc::common::request_canceled, {});
|
|
593
|
+
}
|
|
603
594
|
if (new_session->is_connected()) {
|
|
604
|
-
{
|
|
605
|
-
|
|
606
|
-
|
|
595
|
+
if (!self->publish_busy(new_session, /* leaving_pending */ true)) {
|
|
596
|
+
new_session->stop();
|
|
597
|
+
return cb(errc::common::request_canceled, {});
|
|
607
598
|
}
|
|
608
599
|
cb({}, new_session);
|
|
609
600
|
} else {
|
|
610
|
-
|
|
611
|
-
{
|
|
612
|
-
const std::scoped_lock inner_lock(self->sessions_mutex_);
|
|
613
|
-
self->pending_sessions_[new_session->type()].push_back(new_session);
|
|
614
|
-
}
|
|
615
|
-
self->connect_then_send_pending_op(
|
|
616
|
-
new_session, preferred_node, dispatch_deadline, deadline, cb);
|
|
617
|
-
#else
|
|
618
|
-
self->connect_then_send_pending_op(new_session, preferred_node, deadline, cb);
|
|
619
|
-
#endif
|
|
601
|
+
self->connect_then_send_pending_op(new_session, preferred_node, deadline, std::move(cb));
|
|
620
602
|
}
|
|
621
603
|
} else {
|
|
622
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
623
|
-
auto now = std::chrono::steady_clock::now();
|
|
624
|
-
if (dispatch_deadline < now || deadline < now) {
|
|
625
|
-
session->stop();
|
|
626
|
-
cb(errc::common::unambiguous_timeout, {});
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
#else
|
|
630
604
|
if (deadline < std::chrono::steady_clock::now()) {
|
|
631
605
|
session->stop();
|
|
632
606
|
cb(errc::common::unambiguous_timeout, {});
|
|
633
607
|
return;
|
|
634
608
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
self->busy_sessions_[session->type()].push_back(session);
|
|
639
|
-
self->pending_sessions_[session->type()].remove_if(
|
|
640
|
-
[id = session->id()](const auto& s) -> bool {
|
|
641
|
-
return !s || s->id() == id;
|
|
642
|
-
});
|
|
609
|
+
if (!self->publish_busy(session, /* leaving_pending */ true)) {
|
|
610
|
+
session->stop();
|
|
611
|
+
return cb(errc::common::request_canceled, {});
|
|
643
612
|
}
|
|
644
613
|
cb({}, session);
|
|
645
614
|
}
|
|
646
615
|
});
|
|
647
616
|
}
|
|
648
617
|
|
|
649
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
650
|
-
void add_to_deferred_queue(utils::movable_function<void(error_union)> command)
|
|
651
|
-
{
|
|
652
|
-
const std::scoped_lock lock_for_deferred_commands(deferred_commands_mutex_);
|
|
653
|
-
deferred_commands_.emplace(std::move(command));
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
[[nodiscard]] auto dispatch_timeout() const -> std::chrono::milliseconds
|
|
657
|
-
{
|
|
658
|
-
return dispatch_timeout_;
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
[[nodiscard]] auto is_configured() const -> bool
|
|
662
|
-
{
|
|
663
|
-
return configured_;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
[[nodiscard]] auto last_bootstrap_error() const -> std::optional<impl::bootstrap_error>
|
|
667
|
-
{
|
|
668
|
-
return last_bootstrap_error_;
|
|
669
|
-
}
|
|
670
|
-
#endif
|
|
671
|
-
|
|
672
618
|
private:
|
|
673
619
|
template<typename Request>
|
|
674
620
|
void connect_then_send(std::shared_ptr<http_session> session,
|
|
@@ -681,19 +627,27 @@ private:
|
|
|
681
627
|
cmd,
|
|
682
628
|
preferred_node = std::move(preferred_node),
|
|
683
629
|
reuse_session]() mutable {
|
|
630
|
+
// stop() runs this callback, from close() among others. A stopped session is terminal: a
|
|
631
|
+
// replacement would open a socket and send a request the stop has ended, and with
|
|
632
|
+
// reuse_session the stopped session's connect() would run this callback again inline, an
|
|
633
|
+
// unbounded recursion.
|
|
634
|
+
if (session->is_stopped()) {
|
|
635
|
+
return cmd->invoke_handler(errc::common::request_canceled, {});
|
|
636
|
+
}
|
|
637
|
+
// A session from before a close() gets no replacement and no reconnect: close() may not reach
|
|
638
|
+
// it, so it is stopped here.
|
|
639
|
+
if (self->is_retired(*session)) {
|
|
640
|
+
session->stop();
|
|
641
|
+
return cmd->invoke_handler(errc::common::request_canceled, {});
|
|
642
|
+
}
|
|
684
643
|
if (!session->is_connected()) {
|
|
685
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
686
|
-
auto now = std::chrono::steady_clock::now();
|
|
687
|
-
if (cmd->dispatch_deadline_expiry() < now || cmd->deadline_expiry() < now) {
|
|
688
|
-
// The http command will stop its session when the deadline expires.
|
|
689
|
-
return;
|
|
690
|
-
}
|
|
691
|
-
#else
|
|
692
644
|
if (cmd->deadline_expiry() < std::chrono::steady_clock::now()) {
|
|
693
|
-
// The
|
|
645
|
+
// The command's deadline completes it, but stops only the session it held when it
|
|
646
|
+
// fired, which may be the one this replaced. Stopped here, a replacement does not stay
|
|
647
|
+
// listed pending until close().
|
|
648
|
+
session->stop();
|
|
694
649
|
return;
|
|
695
650
|
}
|
|
696
|
-
#endif
|
|
697
651
|
if (reuse_session) {
|
|
698
652
|
return self->connect_then_send(session, cmd, preferred_node, reuse_session);
|
|
699
653
|
}
|
|
@@ -706,25 +660,97 @@ private:
|
|
|
706
660
|
cmd->invoke_handler(errc::common::service_not_available, {});
|
|
707
661
|
return;
|
|
708
662
|
}
|
|
709
|
-
auto new_session = self->
|
|
663
|
+
auto new_session = self->create_replacement(session->type(), node, *session);
|
|
664
|
+
if (!new_session) {
|
|
665
|
+
return cmd->invoke_handler(errc::common::request_canceled, {});
|
|
666
|
+
}
|
|
710
667
|
cmd->set_command_session(new_session);
|
|
711
668
|
if (new_session->is_connected()) {
|
|
712
|
-
|
|
713
|
-
|
|
669
|
+
if (!self->publish_busy(new_session, /* leaving_pending */ true)) {
|
|
670
|
+
new_session->stop();
|
|
671
|
+
return cmd->invoke_handler(errc::common::request_canceled, {});
|
|
672
|
+
}
|
|
714
673
|
cmd->send_to();
|
|
715
674
|
} else {
|
|
716
675
|
self->connect_then_send(new_session, cmd, preferred_node);
|
|
717
676
|
}
|
|
718
677
|
} else {
|
|
719
|
-
|
|
720
|
-
|
|
678
|
+
if (!self->publish_busy(session, /* leaving_pending */ true)) {
|
|
679
|
+
session->stop();
|
|
680
|
+
return cmd->invoke_handler(errc::common::request_canceled, {});
|
|
681
|
+
}
|
|
682
|
+
// Outside sessions_mutex_: send_to() can complete the command inline (an encode_to
|
|
683
|
+
// failure, or a session already stopped), and the completion calls check_in, which takes
|
|
684
|
+
// it.
|
|
721
685
|
cmd->send_to();
|
|
722
686
|
}
|
|
723
687
|
});
|
|
724
688
|
}
|
|
725
689
|
|
|
726
|
-
|
|
690
|
+
// Lists a connected session as busy unless its stop() has begun or it is from before a close(),
|
|
691
|
+
// and reports whether it did. stop() sets stopped_ before on_stop's cleanup takes
|
|
692
|
+
// sessions_mutex_, so a stop is either seen here or removes the session after it is listed.
|
|
693
|
+
// Tested outside the lock, a stop landing in between would list a session whose cleanup has
|
|
694
|
+
// already run. A refused session is the caller's to stop: close() does not reach one it never had
|
|
695
|
+
// in a list.
|
|
696
|
+
auto publish_busy(const std::shared_ptr<http_session>& session, bool leaving_pending = false)
|
|
697
|
+
-> bool
|
|
698
|
+
{
|
|
699
|
+
const std::scoped_lock lock(sessions_mutex_);
|
|
700
|
+
if (session->pool_generation() != generation_ || session->is_stopped()) {
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
busy_sessions_[session->type()].push_back(session);
|
|
704
|
+
if (leaving_pending) {
|
|
705
|
+
pending_sessions_[session->type()].remove_if([id = session->id()](const auto& s) -> bool {
|
|
706
|
+
return !s || s->id() == id;
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
return true;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
auto current_generation() -> std::uint64_t
|
|
713
|
+
{
|
|
714
|
+
const std::scoped_lock lock(sessions_mutex_);
|
|
715
|
+
return generation_;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Serves a generation that a close() has ended.
|
|
719
|
+
auto is_retired(const http_session& session) -> bool
|
|
720
|
+
{
|
|
721
|
+
return session.pool_generation() != current_generation();
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// A popped idle session goes back into service only if nothing has claimed it and reset_idle()
|
|
725
|
+
// accepts it, which it refuses once the idle timer has fired or a stop() has begun.
|
|
726
|
+
static auto is_reusable(http_session& session) -> bool
|
|
727
|
+
{
|
|
728
|
+
return !session.is_stopping() && session.reset_idle();
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// A failover replacement for `failed`. It serves the same operation, so it belongs to the same
|
|
732
|
+
// generation, and it is listed pending from creation: close() either stops it or has already
|
|
733
|
+
// retired that generation, and then the replacement is discarded and nullptr returned. Unlisted
|
|
734
|
+
// until its connect completes, it would be invisible to a close() whose completion lets the
|
|
735
|
+
// io_context stop.
|
|
736
|
+
auto create_replacement(service_type type, const node_details& node, const http_session& failed)
|
|
737
|
+
-> std::shared_ptr<http_session>
|
|
738
|
+
{
|
|
739
|
+
auto session = create_session(type, node, failed.pool_generation());
|
|
740
|
+
const std::scoped_lock lock(sessions_mutex_);
|
|
741
|
+
if (session->pool_generation() != generation_) {
|
|
742
|
+
return nullptr;
|
|
743
|
+
}
|
|
744
|
+
pending_sessions_[type].push_back(session);
|
|
745
|
+
return session;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
auto create_session(service_type type, const node_details& node, std::uint64_t generation)
|
|
749
|
+
-> std::shared_ptr<http_session>
|
|
727
750
|
{
|
|
751
|
+
// Read before the session takes the TLS context. retire_sessions() runs after the context is
|
|
752
|
+
// replaced, so this epoch is never newer than the context.
|
|
753
|
+
const std::uint64_t tls_epoch = tls_epoch_;
|
|
728
754
|
std::shared_ptr<http_session> session;
|
|
729
755
|
if (options_.enable_tls) {
|
|
730
756
|
session = std::make_shared<http_session>(type,
|
|
@@ -743,7 +769,8 @@ private:
|
|
|
743
769
|
node.port,
|
|
744
770
|
node.canonical_hostname,
|
|
745
771
|
node.canonical_port,
|
|
746
|
-
}
|
|
772
|
+
},
|
|
773
|
+
generation);
|
|
747
774
|
} else {
|
|
748
775
|
session = std::make_shared<http_session>(type,
|
|
749
776
|
client_id_,
|
|
@@ -760,7 +787,8 @@ private:
|
|
|
760
787
|
node.port,
|
|
761
788
|
node.canonical_hostname,
|
|
762
789
|
node.canonical_port,
|
|
763
|
-
}
|
|
790
|
+
},
|
|
791
|
+
generation);
|
|
764
792
|
}
|
|
765
793
|
|
|
766
794
|
session->on_stop([type, id = session->id(), self = this->shared_from_this()]() {
|
|
@@ -789,87 +817,10 @@ private:
|
|
|
789
817
|
}
|
|
790
818
|
}
|
|
791
819
|
});
|
|
820
|
+
session->set_tls_epoch(tls_epoch);
|
|
792
821
|
return session;
|
|
793
822
|
}
|
|
794
823
|
|
|
795
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
796
|
-
template<typename Request, typename Handler>
|
|
797
|
-
void defer_command(Request request, Handler&& handler)
|
|
798
|
-
{
|
|
799
|
-
{
|
|
800
|
-
std::scoped_lock bootstrap_error_lock(last_bootstrap_error_mutex_);
|
|
801
|
-
if (last_bootstrap_error_.has_value()) {
|
|
802
|
-
typename Request::error_context_type ctx{};
|
|
803
|
-
ctx.ec = last_bootstrap_error_.value().ec;
|
|
804
|
-
using response_type = typename Request::encoded_response_type;
|
|
805
|
-
return handler(request.make_response(std::move(ctx), response_type{}));
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
auto cmd = std::make_shared<operations::http_command<Request>>(
|
|
809
|
-
ctx_,
|
|
810
|
-
request,
|
|
811
|
-
tracer_,
|
|
812
|
-
meter_,
|
|
813
|
-
app_telemetry_meter_,
|
|
814
|
-
options_.default_timeout_for(request.type),
|
|
815
|
-
dispatch_timeout_);
|
|
816
|
-
using response_type = typename Request::response_type;
|
|
817
|
-
cmd->start([self = shared_from_this(), cmd, handler = std::forward<Handler>(handler)](
|
|
818
|
-
response_type&& resp) mutable {
|
|
819
|
-
handler(std::move(resp));
|
|
820
|
-
self->check_in(cmd->request.type, cmd->session_);
|
|
821
|
-
});
|
|
822
|
-
CB_LOG_DEBUG(R"(Adding HTTP request to deferred queue: {}, client_context_id="{}")",
|
|
823
|
-
cmd->request.type,
|
|
824
|
-
cmd->client_context_id_);
|
|
825
|
-
add_to_deferred_queue([self = shared_from_this(), cmd, request](error_union err) mutable {
|
|
826
|
-
if (!std::holds_alternative<std::monostate>(err)) {
|
|
827
|
-
using response_type = typename Request::encoded_response_type;
|
|
828
|
-
return cmd->invoke_handler(err, response_type{});
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
// don't do anything if the command wasn't dispatched or has already timed out
|
|
832
|
-
auto now = std::chrono::steady_clock::now();
|
|
833
|
-
if (cmd->dispatch_deadline_expiry() < now || cmd->deadline_expiry() < now) {
|
|
834
|
-
return;
|
|
835
|
-
}
|
|
836
|
-
std::string preferred_node;
|
|
837
|
-
if constexpr (http_traits::supports_sticky_node_v<Request>) {
|
|
838
|
-
if (request.send_to_node) {
|
|
839
|
-
preferred_node = *request.send_to_node;
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
auto [error, session] = self->check_out(request.type, preferred_node);
|
|
843
|
-
if (error) {
|
|
844
|
-
using response_type = typename Request::encoded_response_type;
|
|
845
|
-
return cmd->invoke_handler(error, response_type{});
|
|
846
|
-
}
|
|
847
|
-
cmd->set_command_session(session);
|
|
848
|
-
if (!session->is_connected()) {
|
|
849
|
-
self->connect_then_send(session, cmd, preferred_node);
|
|
850
|
-
} else {
|
|
851
|
-
cmd->send_to();
|
|
852
|
-
}
|
|
853
|
-
});
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
void drain_deferred_queue(error_union err)
|
|
857
|
-
{
|
|
858
|
-
std::queue<utils::movable_function<void(error_union)>> commands{};
|
|
859
|
-
{
|
|
860
|
-
const std::scoped_lock lock(deferred_commands_mutex_);
|
|
861
|
-
std::swap(deferred_commands_, commands);
|
|
862
|
-
}
|
|
863
|
-
if (!commands.empty()) {
|
|
864
|
-
CB_LOG_TRACE("Draining deferred operation queue, size={}", commands.size());
|
|
865
|
-
}
|
|
866
|
-
while (!commands.empty()) {
|
|
867
|
-
commands.front()(err);
|
|
868
|
-
commands.pop();
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
#endif
|
|
872
|
-
|
|
873
824
|
auto next_node(service_type type) -> node_details
|
|
874
825
|
{
|
|
875
826
|
std::scoped_lock lock(config_mutex_);
|
|
@@ -968,15 +919,11 @@ private:
|
|
|
968
919
|
std::size_t next_index_{ 0 };
|
|
969
920
|
std::mutex next_index_mutex_{};
|
|
970
921
|
std::mutex sessions_mutex_{};
|
|
922
|
+
// Advanced by close(); guarded by sessions_mutex_. A session carries the generation it serves,
|
|
923
|
+
// and check_in and publish_busy refuse one from an earlier generation.
|
|
924
|
+
std::uint64_t generation_{ 0 };
|
|
925
|
+
// Advanced by retire_sessions(). Unlike generation_, it ends no in-flight request.
|
|
926
|
+
std::atomic<std::uint64_t> tls_epoch_{ 0 };
|
|
971
927
|
query_cache query_cache_{};
|
|
972
|
-
#ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
|
|
973
|
-
std::atomic_bool configured_{ false };
|
|
974
|
-
std::chrono::milliseconds dispatch_timeout_{};
|
|
975
|
-
std::atomic_bool allow_fast_fail_{ true };
|
|
976
|
-
std::queue<utils::movable_function<void(error_union)>> deferred_commands_{};
|
|
977
|
-
std::mutex deferred_commands_mutex_{};
|
|
978
|
-
std::optional<impl::bootstrap_error> last_bootstrap_error_;
|
|
979
|
-
std::mutex last_bootstrap_error_mutex_{};
|
|
980
|
-
#endif
|
|
981
928
|
};
|
|
982
929
|
} // namespace couchbase::core::io
|