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
|
@@ -18,15 +18,22 @@
|
|
|
18
18
|
|
|
19
19
|
#include "active_transaction_record.hxx"
|
|
20
20
|
#include "attempt_context_impl.hxx"
|
|
21
|
+
#include "core/cluster.hxx"
|
|
21
22
|
#include "core/document_id.hxx"
|
|
22
23
|
#include "core/transactions/error_class.hxx"
|
|
23
24
|
#include "core/transactions/exceptions.hxx"
|
|
24
25
|
#include "forward_compat.hxx"
|
|
26
|
+
#include "get_multi_fetch.hxx"
|
|
27
|
+
#include "get_multi_transaction_id.hxx"
|
|
28
|
+
#include "internal/utils.hxx"
|
|
29
|
+
|
|
30
|
+
#include <asio/steady_timer.hpp>
|
|
25
31
|
|
|
26
|
-
#include <algorithm>
|
|
27
32
|
#include <chrono>
|
|
28
33
|
#include <exception>
|
|
34
|
+
#include <memory>
|
|
29
35
|
#include <queue>
|
|
36
|
+
#include <set>
|
|
30
37
|
#include <vector>
|
|
31
38
|
|
|
32
39
|
namespace couchbase::core::transactions
|
|
@@ -82,31 +89,6 @@ struct get_multi_spec {
|
|
|
82
89
|
core::document_id id{};
|
|
83
90
|
};
|
|
84
91
|
|
|
85
|
-
// TODO(SA): I believe it should be accessible as an object directly
|
|
86
|
-
struct transaction_id {
|
|
87
|
-
std::string transaction;
|
|
88
|
-
std::string attempt;
|
|
89
|
-
std::string operation;
|
|
90
|
-
|
|
91
|
-
auto operator==(const transaction_id& other) const -> bool
|
|
92
|
-
{
|
|
93
|
-
return transaction == other.transaction && attempt == other.attempt &&
|
|
94
|
-
operation == other.operation;
|
|
95
|
-
}
|
|
96
|
-
auto operator<(const transaction_id& other) const -> bool
|
|
97
|
-
{
|
|
98
|
-
if (transaction != other.transaction) {
|
|
99
|
-
return transaction < other.transaction;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (attempt != other.attempt) {
|
|
103
|
-
return attempt < other.attempt;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return operation < other.operation;
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
|
|
110
92
|
struct get_multi_result {
|
|
111
93
|
get_multi_spec spec;
|
|
112
94
|
std::optional<transaction_get_result> get_result{};
|
|
@@ -128,17 +110,9 @@ struct get_multi_result {
|
|
|
128
110
|
[[nodiscard]] auto extract_transaction_id() const -> std::optional<transaction_id>
|
|
129
111
|
{
|
|
130
112
|
if (const auto& result = get_result; result) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (txn && atmpt && op) {
|
|
136
|
-
return transaction_id{
|
|
137
|
-
txn.value(),
|
|
138
|
-
atmpt.value(),
|
|
139
|
-
op.value(),
|
|
140
|
-
};
|
|
141
|
-
}
|
|
113
|
+
return make_transaction_id(result->links().staged_transaction_id(),
|
|
114
|
+
result->links().staged_attempt_id(),
|
|
115
|
+
result->links().staged_operation_id());
|
|
142
116
|
}
|
|
143
117
|
return {};
|
|
144
118
|
}
|
|
@@ -191,22 +165,33 @@ classify_error(const std::exception_ptr& err) -> classified_error
|
|
|
191
165
|
return classified_error{ error_class::FAIL_OTHER, external_exception::UNKNOWN };
|
|
192
166
|
}
|
|
193
167
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Drives a single transactional get-multi operation: fetch N documents and, where there is
|
|
170
|
+
* evidence of read skew, resolve it to return a consistent snapshot.
|
|
171
|
+
*
|
|
172
|
+
* This implements the algorithm in the "ExtGetMulti" transactions spec
|
|
173
|
+
* (couchbase-transactions-specs/transactions-get-multi.md). The spec is written as a set of
|
|
174
|
+
* function-like sections that call one another; the methods here mirror those sections:
|
|
175
|
+
* - fetch_multiple_documents() -> "Fetching multiple documents"
|
|
176
|
+
* - fetch_individual_document() -> "Fetching an individual document"
|
|
177
|
+
* - disambiguate_results() -> "Document disambiguation"
|
|
178
|
+
* - resolve_read_skew() -> "Read skew resolution"
|
|
179
|
+
* The driving loop (spec "GetMulti" / "Implementation") is realised asynchronously: each fetch
|
|
180
|
+
* round completes into disambiguate_results(), which either finishes the operation or schedules a
|
|
181
|
+
* further round via retry()/reset_and_retry(). The spec's control-flow signals (Continue, Retry,
|
|
182
|
+
* ResetAndRetry, Completed, BoundExceeded) are expressed as those direct method calls rather than
|
|
183
|
+
* as returned values.
|
|
184
|
+
*/
|
|
206
185
|
class get_multi_operation : public std::enable_shared_from_this<get_multi_operation>
|
|
207
186
|
{
|
|
208
187
|
public:
|
|
209
188
|
static constexpr std::size_t default_number_of_concurrent_requests{ 100 };
|
|
189
|
+
// Spec "Fetching an individual document": bound each read by min(remaining, the default 2.5s KV
|
|
190
|
+
// read timeout).
|
|
191
|
+
static constexpr std::chrono::milliseconds get_multi_key_value_read_timeout{ 2500 };
|
|
192
|
+
// Backstop on per-document transient retries; the read-skew bound is the primary terminator, so
|
|
193
|
+
// this only caps a document that keeps failing before the bound elapses.
|
|
194
|
+
static constexpr std::size_t get_multi_max_transient_retries{ 100 };
|
|
210
195
|
|
|
211
196
|
~get_multi_operation() = default;
|
|
212
197
|
get_multi_operation(const get_multi_operation&) = delete;
|
|
@@ -263,6 +248,14 @@ public:
|
|
|
263
248
|
--responses_left_;
|
|
264
249
|
}
|
|
265
250
|
|
|
251
|
+
// Mark one in-flight fetch complete without touching results_[index]. Used when the read-skew
|
|
252
|
+
// bound is exceeded: the value already fetched for this spec (if any) must be preserved for the
|
|
253
|
+
// best-effort snapshot instead of being overwritten with an empty result.
|
|
254
|
+
void note_fetch_complete_without_result()
|
|
255
|
+
{
|
|
256
|
+
--responses_left_;
|
|
257
|
+
}
|
|
258
|
+
|
|
266
259
|
auto pop_next_spec() -> std::optional<get_multi_spec>
|
|
267
260
|
{
|
|
268
261
|
if (to_fetch_.empty()) {
|
|
@@ -280,6 +273,10 @@ public:
|
|
|
280
273
|
}
|
|
281
274
|
}
|
|
282
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Realises the spec's "Retry" signal: refetch the given subset of specs (retaining the rest of
|
|
278
|
+
* the operation state and the current phase), then re-run disambiguation when they complete.
|
|
279
|
+
*/
|
|
283
280
|
void retry(std::queue<get_multi_spec> specs)
|
|
284
281
|
{
|
|
285
282
|
to_fetch_ = std::move(specs);
|
|
@@ -287,13 +284,21 @@ public:
|
|
|
287
284
|
fetch_multiple_documents();
|
|
288
285
|
}
|
|
289
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Realises the spec's "ResetAndRetry" signal: discard the fetched results and refetch every
|
|
289
|
+
* original spec from scratch (the state is too complex to resolve incrementally).
|
|
290
|
+
*/
|
|
290
291
|
void reset_and_retry()
|
|
291
292
|
{
|
|
292
293
|
std::queue<get_multi_spec> to_fetch;
|
|
293
294
|
for (const auto& spec : specs_) {
|
|
294
295
|
to_fetch.emplace(spec);
|
|
295
296
|
}
|
|
297
|
+
// Restore one slot per spec before refetching: results are stored by spec index, so the
|
|
298
|
+
// container must stay sized to the spec count. A bare clear() would leave size() at 0 and the
|
|
299
|
+
// subsequent index assignments from the refetch handlers would be out of bounds.
|
|
296
300
|
results_.clear();
|
|
301
|
+
results_.resize(specs_.size());
|
|
297
302
|
if (phase_ != get_multi_phase::first_doc_fetch) {
|
|
298
303
|
phase_ = get_multi_phase::subsequent_to_first_doc_fetch;
|
|
299
304
|
}
|
|
@@ -305,6 +310,13 @@ public:
|
|
|
305
310
|
invoke_callback();
|
|
306
311
|
}
|
|
307
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Implements the spec's "Read skew resolution" section. Reaches here only when exactly one other
|
|
315
|
+
* transaction T1 is involved in the fetched documents. Fetches T1's ATR entry and, based on its
|
|
316
|
+
* state and the current phase, either: returns the documents as-is (T1 not committed), refetches
|
|
317
|
+
* the documents we missed were in T1 (committed, "SubsequentToFirstDocFetch"/"DiscoveredDocsInT1"
|
|
318
|
+
* branches), disambiguates a missing ATR entry, or resets and retries.
|
|
319
|
+
*/
|
|
308
320
|
void resolve_read_skew()
|
|
309
321
|
{
|
|
310
322
|
std::string other_attempt_id;
|
|
@@ -356,82 +368,114 @@ public:
|
|
|
356
368
|
}
|
|
357
369
|
}
|
|
358
370
|
|
|
371
|
+
std::optional<atr_entry> attempt{};
|
|
359
372
|
if (atr) {
|
|
360
|
-
std::optional<atr_entry> attempt{};
|
|
361
373
|
for (const auto& entry : atr->entries()) {
|
|
362
374
|
if (entry.attempt_id() == other_attempt_id) {
|
|
363
375
|
attempt = entry;
|
|
364
376
|
}
|
|
365
377
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
if (result.has_transactional_metadata() &&
|
|
378
|
-
(contains_mutation(attempt->inserted_ids(), result.spec.id) ||
|
|
379
|
-
contains_mutation(attempt->replaced_ids(), result.spec.id) ||
|
|
380
|
-
contains_mutation(attempt->removed_ids(), result.spec.id))) {
|
|
381
|
-
were_in_t1.emplace_back(result);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
if (were_in_t1.empty()) {
|
|
386
|
-
for (auto& result : fetched_in_t1) {
|
|
387
|
-
result->copy_content_from_staged_operation_into_result();
|
|
388
|
-
}
|
|
389
|
-
return self->completed();
|
|
390
|
-
}
|
|
378
|
+
}
|
|
379
|
+
if (attempt) {
|
|
380
|
+
// Reading T1's ATR entry must run the GETS_READING_ATR forward-compatibility check, as
|
|
381
|
+
// the normal MAV get path does. The raw get_multi fetch performs no ATR read, so this
|
|
382
|
+
// check would otherwise be skipped entirely.
|
|
383
|
+
if (auto fc_err = check_forward_compat(forward_compat_stage::GETS_READING_ATR,
|
|
384
|
+
attempt->forward_compat());
|
|
385
|
+
fc_err) {
|
|
386
|
+
return self->invoke_callback(std::make_exception_ptr(fc_err.value()));
|
|
387
|
+
}
|
|
388
|
+
auto state = attempt->state();
|
|
391
389
|
|
|
392
|
-
|
|
390
|
+
if (state == attempt_state::PENDING || state == attempt_state::ABORTED) {
|
|
391
|
+
return self->completed();
|
|
392
|
+
}
|
|
393
393
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
394
|
+
if (state == attempt_state::COMMITTED) {
|
|
395
|
+
if (self->phase_ == get_multi_phase::subsequent_to_first_doc_fetch) {
|
|
396
|
+
// The read-skew victims are documents we fetched WITHOUT T1's metadata (e.g. read
|
|
397
|
+
// before T1 committed) yet which T1's ATR entry records as mutated. They must be
|
|
398
|
+
// re-fetched to obtain a consistent snapshot. Mirror the reference SDK: build this
|
|
399
|
+
// set from the docs not fetched as part of T1 and filter by T1's ATR document list
|
|
400
|
+
// -- not from the docs that happen to already carry T1's metadata (those are the
|
|
401
|
+
// ones we have already resolved, not the stale ones).
|
|
402
|
+
std::vector<get_multi_result> were_in_t1;
|
|
403
|
+
for (auto& result : self->results_) {
|
|
404
|
+
if (is_read_skew_victim(result.doc_exists(),
|
|
405
|
+
result.extract_transaction_id(),
|
|
406
|
+
other_attempt_id,
|
|
407
|
+
result.spec.id,
|
|
408
|
+
attempt->inserted_ids(),
|
|
409
|
+
attempt->replaced_ids(),
|
|
410
|
+
attempt->removed_ids())) {
|
|
411
|
+
were_in_t1.emplace_back(result);
|
|
397
412
|
}
|
|
398
|
-
return self->retry(std::move(to_fetch));
|
|
399
413
|
}
|
|
400
|
-
|
|
414
|
+
|
|
415
|
+
if (were_in_t1.empty()) {
|
|
401
416
|
for (auto& result : fetched_in_t1) {
|
|
402
417
|
result->copy_content_from_staged_operation_into_result();
|
|
403
418
|
}
|
|
404
419
|
return self->completed();
|
|
405
420
|
}
|
|
406
|
-
}
|
|
407
421
|
|
|
408
|
-
|
|
409
|
-
}
|
|
422
|
+
self->phase_ = get_multi_phase::discovered_docs_in_t1;
|
|
410
423
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
424
|
+
std::queue<get_multi_spec> to_fetch;
|
|
425
|
+
for (const auto& result : were_in_t1) {
|
|
426
|
+
to_fetch.emplace(result.spec);
|
|
427
|
+
}
|
|
428
|
+
return self->retry(std::move(to_fetch));
|
|
429
|
+
}
|
|
430
|
+
if (self->phase_ == get_multi_phase::discovered_docs_in_t1) {
|
|
431
|
+
for (auto& result : fetched_in_t1) {
|
|
432
|
+
result->copy_content_from_staged_operation_into_result();
|
|
433
|
+
}
|
|
434
|
+
return self->completed();
|
|
415
435
|
}
|
|
416
|
-
return self->completed();
|
|
417
436
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
437
|
+
|
|
438
|
+
return self->reset_and_retry();
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// There is no ATR record for T1: either the ATR document holds no entry for
|
|
442
|
+
// T1, or the ATR document itself is absent (get_atr yields an empty record on
|
|
443
|
+
// document-not-found). Both mean the same thing and are handled identically
|
|
444
|
+
// here, per the spec's "ATR entry missing" case.
|
|
445
|
+
if (self->phase_ == get_multi_phase::resolving_t1_atr_entry_missing) {
|
|
446
|
+
if (fetched_in_t1.empty()) {
|
|
447
|
+
return self->reset_and_retry();
|
|
422
448
|
}
|
|
423
|
-
return self->
|
|
449
|
+
return self->completed();
|
|
450
|
+
}
|
|
451
|
+
self->phase_ = get_multi_phase::resolving_t1_atr_entry_missing;
|
|
452
|
+
std::queue<get_multi_spec> to_fetch;
|
|
453
|
+
for (const auto& result : fetched_in_t1) {
|
|
454
|
+
to_fetch.emplace(result->spec);
|
|
424
455
|
}
|
|
456
|
+
return self->retry(std::move(to_fetch));
|
|
425
457
|
});
|
|
426
458
|
}
|
|
427
459
|
|
|
460
|
+
/**
|
|
461
|
+
* Implements the spec's "Document disambiguation" section. Runs once a fetch round has completed.
|
|
462
|
+
* Counts how many distinct other transactions the fetched documents are involved in: zero means
|
|
463
|
+
* no detectable read skew (done); exactly one proceeds to read-skew resolution; two or more is
|
|
464
|
+
* too complex to resolve and resets. Also enforces the operation deadline (see spec "Timeouts").
|
|
465
|
+
*/
|
|
428
466
|
void disambiguate_results()
|
|
429
467
|
{
|
|
430
468
|
if (std::chrono::steady_clock::now() >= deadline_) {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
469
|
+
// The read-skew resolution bound has been exceeded (spec "Global signal handling" for a
|
|
470
|
+
// BoundExceeded signal). Disambiguation is only entered once a fetch round has fully
|
|
471
|
+
// completed, so results_ holds an entry for every spec. Per the spec, when we have something
|
|
472
|
+
// for every document we return it as a best-effort snapshot -- which may still contain read
|
|
473
|
+
// skew we ran out of time to resolve -- rather than failing the operation. Any genuine
|
|
474
|
+
// per-document fetch error recorded in results_ is still surfaced by the completion callback.
|
|
475
|
+
// The bound is checked here between fetch rounds; an individual fetch still runs to the
|
|
476
|
+
// regular key-value timeout, so a single slow document can overshoot the bound within a
|
|
477
|
+
// round.
|
|
478
|
+
return completed();
|
|
435
479
|
}
|
|
436
480
|
|
|
437
481
|
std::set<transaction_id> transaction_ids{};
|
|
@@ -457,50 +501,207 @@ public:
|
|
|
457
501
|
}
|
|
458
502
|
}
|
|
459
503
|
|
|
460
|
-
|
|
504
|
+
/**
|
|
505
|
+
* Implements the spec's "Fetching an individual document" section. Performs a raw lookupIn for
|
|
506
|
+
* the document body and transactional metadata (explicitly not a MAV read), runs the
|
|
507
|
+
* forward-compatibility checks the normal get path would, and records the result. As each fetch
|
|
508
|
+
* completes it pulls the next queued spec, and once the round is drained it advances the deadline
|
|
509
|
+
* per the mode and hands off to disambiguate_results().
|
|
510
|
+
*/
|
|
511
|
+
void fetch_individual_document(const get_multi_spec& spec,
|
|
512
|
+
std::shared_ptr<async_exp_delay> backoff = nullptr)
|
|
461
513
|
{
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
514
|
+
// Per spec, a read-skew-resolution fetch is bounded by min(time left before the bound, the KV
|
|
515
|
+
// read timeout); the first-doc fetch has no bound yet, so it uses the SDK's default read
|
|
516
|
+
// timeout (a nullopt override).
|
|
517
|
+
std::optional<std::chrono::milliseconds> timeout{};
|
|
518
|
+
if (phase_ != get_multi_phase::first_doc_fetch) {
|
|
519
|
+
timeout = get_multi_fetch_timeout(deadline_ - std::chrono::steady_clock::now(),
|
|
520
|
+
get_multi_key_value_read_timeout);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// get_multi must fetch the raw document body and transactional metadata with a plain lookupIn
|
|
524
|
+
// -- explicitly NOT a MAV read, which would perform its own ATR lookup and resolution and rob
|
|
525
|
+
// the orchestrator of the metadata it needs to detect and resolve read skew itself.
|
|
526
|
+
attempt_->get_doc(
|
|
527
|
+
spec.id,
|
|
528
|
+
use_replicas_,
|
|
529
|
+
timeout,
|
|
530
|
+
[spec, backoff, self = shared_from_this()](
|
|
531
|
+
std::optional<error_class> ec,
|
|
532
|
+
std::optional<external_exception> cause,
|
|
533
|
+
std::optional<std::string> message,
|
|
534
|
+
std::optional<transaction_get_result> res) mutable {
|
|
535
|
+
if (!self->callback_) {
|
|
536
|
+
// The operation already completed (e.g. a sibling fetch hit a forward-compat failure and
|
|
537
|
+
// invoked the callback, moving results_ out). Concurrently-dispatched fetches can still
|
|
538
|
+
// complete afterwards; ignore them so we never index the moved-from results_ vector.
|
|
469
539
|
return;
|
|
470
540
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
541
|
+
|
|
542
|
+
if (res) {
|
|
543
|
+
// The raw fetch does not perform MAV, so the forward-compatibility checks the normal get
|
|
544
|
+
// path would run must be done here. Per spec, run two checks in order: GET_MULTI_GET
|
|
545
|
+
// ("GM_G", so newer clients can block getMulti specifically) then GETS ("G", which older
|
|
546
|
+
// clients use and which anything blocking get likely needs to block getMulti too).
|
|
547
|
+
for (const auto stage :
|
|
548
|
+
{ forward_compat_stage::GET_MULTI_GET, forward_compat_stage::GETS }) {
|
|
549
|
+
if (auto fc_err = check_forward_compat(stage, res->links().forward_compat()); fc_err) {
|
|
550
|
+
self->invoke_callback(std::make_exception_ptr(fc_err.value()));
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
self->handle_individual_document_success(spec, std::move(res));
|
|
555
|
+
return self->advance_after_fetch();
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// No document: classify the failure per the spec's "Fetching an individual document /
|
|
559
|
+
// Error handling" section (classify_get_multi_fetch_error documents the error-model
|
|
560
|
+
// adaptation).
|
|
561
|
+
const auto fetch_outcome =
|
|
562
|
+
classify_get_multi_fetch_error(ec, cause, self->read_skew_bound_exceeded());
|
|
563
|
+
switch (fetch_outcome) {
|
|
564
|
+
case get_multi_fetch_outcome::document_absent:
|
|
565
|
+
// Not a failure: record the document as absent so the round completes.
|
|
566
|
+
self->handle_individual_document_error(spec, nullptr);
|
|
567
|
+
return self->advance_after_fetch();
|
|
568
|
+
case get_multi_fetch_outcome::bound_exceeded:
|
|
569
|
+
switch (get_multi_bound_exceeded_action(self->results_[spec.index].doc_exists())) {
|
|
570
|
+
case bound_exceeded_action::preserve_prior_value:
|
|
571
|
+
// Best-effort: a value fetched for this spec in an earlier round must stay in the
|
|
572
|
+
// snapshot, so mark the fetch complete without overwriting results_[index].
|
|
573
|
+
self->note_fetch_complete_without_result();
|
|
574
|
+
break;
|
|
575
|
+
case bound_exceeded_action::fail_retryable: {
|
|
576
|
+
// No value held for this spec (e.g. reset_and_retry() cleared the slots and the
|
|
577
|
+
// re-fetch hit a transient at the already-elapsed bound). A transient is never a
|
|
578
|
+
// document-not-found, so record it as retryable rather than leaving the slot blank
|
|
579
|
+
// -- a blank would be misreported as an absent document. The retry drives the
|
|
580
|
+
// enclosing transaction to try again.
|
|
581
|
+
auto err = transaction_operation_failed(
|
|
582
|
+
ec.value(),
|
|
583
|
+
message.value_or("get_multi read-skew bound exceeded before the "
|
|
584
|
+
"document could be fetched"))
|
|
585
|
+
.retry();
|
|
586
|
+
if (cause) {
|
|
587
|
+
err.cause(cause.value());
|
|
588
|
+
}
|
|
589
|
+
self->handle_individual_document_error(spec, std::make_exception_ptr(err));
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return self->advance_after_fetch();
|
|
594
|
+
case get_multi_fetch_outcome::retry_after_backoff:
|
|
595
|
+
return self->retry_individual_document_after_backoff(
|
|
596
|
+
spec, std::move(backoff), ec, cause, message);
|
|
597
|
+
case get_multi_fetch_outcome::fail_expired:
|
|
598
|
+
case get_multi_fetch_outcome::fail_without_rollback:
|
|
599
|
+
case get_multi_fetch_outcome::fail_with_rollback: {
|
|
600
|
+
// Build the failure once; the outcome only selects the modifier applied to it.
|
|
601
|
+
auto err = transaction_operation_failed(
|
|
602
|
+
ec.value(), message.value_or("error fetching document in get_multi"));
|
|
603
|
+
if (cause) {
|
|
604
|
+
err.cause(cause.value());
|
|
605
|
+
}
|
|
606
|
+
if (fetch_outcome == get_multi_fetch_outcome::fail_expired) {
|
|
607
|
+
err = err.expired();
|
|
608
|
+
} else if (fetch_outcome == get_multi_fetch_outcome::fail_without_rollback) {
|
|
609
|
+
err = err.no_rollback();
|
|
610
|
+
}
|
|
611
|
+
self->handle_individual_document_error(spec, std::make_exception_ptr(err));
|
|
612
|
+
return self->advance_after_fetch();
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// True once the read-skew resolution bound has elapsed. There is no bound during the first fetch
|
|
619
|
+
// round (it uses the SDK read timeout), so a transient error there always retries rather than
|
|
620
|
+
// being treated as bound-exceeded.
|
|
621
|
+
[[nodiscard]] auto read_skew_bound_exceeded() const -> bool
|
|
622
|
+
{
|
|
623
|
+
return phase_ != get_multi_phase::first_doc_fetch &&
|
|
624
|
+
std::chrono::steady_clock::now() >= deadline_;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// Spec "Fetching an individual document / Error handling": a transient failure retries this fetch
|
|
628
|
+
// after an exponential backoff starting from 1ms. The backoff is threaded through the retries of
|
|
629
|
+
// one document so the delay grows across them; a fresh document starts a new one. If it is
|
|
630
|
+
// exhausted the document has stayed transiently unavailable for too long, so retry the whole
|
|
631
|
+
// transaction.
|
|
632
|
+
void retry_individual_document_after_backoff(const get_multi_spec& spec,
|
|
633
|
+
std::shared_ptr<async_exp_delay> backoff,
|
|
634
|
+
std::optional<error_class> last_ec,
|
|
635
|
+
std::optional<external_exception> last_cause,
|
|
636
|
+
std::optional<std::string> last_message)
|
|
637
|
+
{
|
|
638
|
+
if (!backoff) {
|
|
639
|
+
backoff = std::make_shared<async_exp_delay>(
|
|
640
|
+
std::make_shared<asio::steady_timer>(attempt_->cluster_ref().io_context()),
|
|
641
|
+
std::chrono::milliseconds{ 1 },
|
|
642
|
+
std::chrono::milliseconds{ 100 },
|
|
643
|
+
get_multi_max_transient_retries);
|
|
644
|
+
}
|
|
645
|
+
(*backoff)([spec, backoff, last_ec, last_cause, last_message, self = shared_from_this()](
|
|
646
|
+
std::exception_ptr err) mutable {
|
|
647
|
+
if (!self->callback_) {
|
|
648
|
+
return;
|
|
477
649
|
}
|
|
478
|
-
if (
|
|
479
|
-
|
|
480
|
-
|
|
650
|
+
if (err) {
|
|
651
|
+
// Backoff exhausted: the document stayed transiently unavailable for too long. Retry the
|
|
652
|
+
// whole transaction, preserving the last fetch's error class/cause/message so the failure
|
|
653
|
+
// keeps its diagnostics (timeout vs ambiguous vs transient) instead of a generic one.
|
|
654
|
+
auto tof = transaction_operation_failed(
|
|
655
|
+
last_ec.value_or(FAIL_TRANSIENT),
|
|
656
|
+
last_message.value_or("get_multi document fetch kept failing transiently"))
|
|
657
|
+
.retry();
|
|
658
|
+
if (last_cause) {
|
|
659
|
+
tof.cause(last_cause.value());
|
|
481
660
|
}
|
|
661
|
+
self->handle_individual_document_error(spec, std::make_exception_ptr(tof));
|
|
662
|
+
return self->advance_after_fetch();
|
|
663
|
+
}
|
|
664
|
+
self->fetch_individual_document(spec, std::move(backoff));
|
|
665
|
+
});
|
|
666
|
+
}
|
|
482
667
|
|
|
483
|
-
|
|
668
|
+
// Common tail of a completed individual fetch: start the next queued spec, or, once the round is
|
|
669
|
+
// drained, set the read-skew bound (once, on leaving the first-doc-fetch phase) and hand off to
|
|
670
|
+
// disambiguation. The bound is set only on the first-doc-fetch -> subsequent transition and left
|
|
671
|
+
// fixed for the remaining rounds; recomputing it every round in latency mode would push the 100ms
|
|
672
|
+
// bound indefinitely into the future and defeat the best-effort BoundExceeded return.
|
|
673
|
+
void advance_after_fetch()
|
|
674
|
+
{
|
|
675
|
+
if (auto next_spec = pop_next_spec(); next_spec) {
|
|
676
|
+
return fetch_individual_document(next_spec.value());
|
|
677
|
+
}
|
|
678
|
+
if (responses_left_ == 0) {
|
|
679
|
+
if (phase_ == get_multi_phase::first_doc_fetch) {
|
|
680
|
+
switch (mode_) {
|
|
484
681
|
case get_multi_mode::disable_read_skew_detection:
|
|
485
|
-
|
|
682
|
+
break;
|
|
486
683
|
case get_multi_mode::prioritise_latency:
|
|
487
|
-
|
|
684
|
+
deadline_ = std::chrono::steady_clock::now() + std::chrono::milliseconds{ 100 };
|
|
488
685
|
break;
|
|
489
686
|
case get_multi_mode::prioritise_read_skew_detection:
|
|
490
|
-
|
|
687
|
+
deadline_ = attempt_->expiry_time();
|
|
491
688
|
break;
|
|
492
689
|
}
|
|
690
|
+
phase_ = get_multi_phase::subsequent_to_first_doc_fetch;
|
|
691
|
+
}
|
|
493
692
|
|
|
494
|
-
|
|
693
|
+
if (mode_ == get_multi_mode::disable_read_skew_detection) {
|
|
694
|
+
return invoke_callback();
|
|
495
695
|
}
|
|
496
|
-
|
|
497
|
-
if (use_replicas_) {
|
|
498
|
-
attempt_->get_replica_from_preferred_server_group(spec.id, std::move(handler));
|
|
499
|
-
} else {
|
|
500
|
-
attempt_->get_optional(spec.id, std::move(handler));
|
|
696
|
+
disambiguate_results();
|
|
501
697
|
}
|
|
502
698
|
}
|
|
503
699
|
|
|
700
|
+
/**
|
|
701
|
+
* Implements the spec's "Fetching multiple documents" section. Schedules the queued specs to be
|
|
702
|
+
* fetched concurrently, with no more than default_number_of_concurrent_requests in flight at
|
|
703
|
+
* once; each completing fetch starts the next, so the cap is maintained without a barrier.
|
|
704
|
+
*/
|
|
504
705
|
void fetch_multiple_documents()
|
|
505
706
|
{
|
|
506
707
|
std::size_t requests_scheduled{ 0 };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021-Present Couchbase, Inc.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
#pragma once
|
|
17
|
+
|
|
18
|
+
#include <optional>
|
|
19
|
+
#include <string>
|
|
20
|
+
|
|
21
|
+
namespace couchbase::core::transactions
|
|
22
|
+
{
|
|
23
|
+
// Identifies the transaction attempt a document is staged in. Identity is the (transaction,
|
|
24
|
+
// attempt) pair only: the per-mutation operation id is deliberately NOT part of it. Read-skew
|
|
25
|
+
// detection groups the fetched documents by the *transaction* they belong to (spec "Document
|
|
26
|
+
// disambiguation"), and two documents staged by the same attempt always carry different operation
|
|
27
|
+
// ids -- including the operation id here would make them count as separate transactions and send
|
|
28
|
+
// disambiguation down the "two or more transactions, too complex" reset-and-retry path forever.
|
|
29
|
+
struct transaction_id {
|
|
30
|
+
std::string transaction;
|
|
31
|
+
std::string attempt;
|
|
32
|
+
|
|
33
|
+
auto operator==(const transaction_id& other) const -> bool
|
|
34
|
+
{
|
|
35
|
+
return transaction == other.transaction && attempt == other.attempt;
|
|
36
|
+
}
|
|
37
|
+
auto operator<(const transaction_id& other) const -> bool
|
|
38
|
+
{
|
|
39
|
+
if (transaction != other.transaction) {
|
|
40
|
+
return transaction < other.transaction;
|
|
41
|
+
}
|
|
42
|
+
return attempt < other.attempt;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Build a transaction_id from the staged (transaction, attempt, operation) ids carried on a
|
|
47
|
+
// document's transactional metadata. All three must be present -- a document genuinely staged in a
|
|
48
|
+
// transaction always carries the full triplet, so a missing id means the document is not staged --
|
|
49
|
+
// but the operation id is deliberately dropped from the resulting identity (see transaction_id).
|
|
50
|
+
// Returns nullopt if any of the three ids is absent.
|
|
51
|
+
[[nodiscard]] inline auto
|
|
52
|
+
make_transaction_id(const std::optional<std::string>& transaction,
|
|
53
|
+
const std::optional<std::string>& attempt,
|
|
54
|
+
const std::optional<std::string>& operation) -> std::optional<transaction_id>
|
|
55
|
+
{
|
|
56
|
+
if (transaction && attempt && operation) {
|
|
57
|
+
return transaction_id{ transaction.value(), attempt.value() };
|
|
58
|
+
}
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
} // namespace couchbase::core::transactions
|