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
|
@@ -69,11 +69,31 @@ public:
|
|
|
69
69
|
{
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* @brief Determines whether this ATR entry's transaction has exceeded its configured timeout.
|
|
74
|
+
*
|
|
75
|
+
* Uses the ATR document's CAS as a server-side clock (CAS encodes a server timestamp in
|
|
76
|
+
* nanoseconds). The elapsed time is computed as:
|
|
77
|
+
* @code elapsed_ms = (cas / 1_000_000) - timestamp_start_ms @endcode
|
|
78
|
+
*
|
|
79
|
+
* Returns @c false conservatively if either @c timestamp_start_ms or @c expires_after_ms are
|
|
80
|
+
* absent — this avoids spurious expiry decisions when the ATR entry has not yet been fully
|
|
81
|
+
* initialised or when the CAS has not advanced past the recorded start time.
|
|
82
|
+
*
|
|
83
|
+
* @param safety_margin Additional milliseconds added to the configured TTL before declaring
|
|
84
|
+
* the entry expired. Useful to declare expiry slightly early in order
|
|
85
|
+
* to avoid races with the owning client.
|
|
86
|
+
* @return @c true if the transaction has exceeded @c (expires_after_ms + safety_margin) of
|
|
87
|
+
* elapsed CAS-time since @c timestamp_start_ms.
|
|
88
|
+
*/
|
|
72
89
|
[[nodiscard]] bool has_expired(std::uint32_t safety_margin = 0) const
|
|
73
90
|
{
|
|
74
91
|
std::uint64_t cas_ms = cas_ / 1000000;
|
|
75
|
-
if (timestamp_start_ms_ && cas_ms > *timestamp_start_ms_) {
|
|
76
|
-
std::
|
|
92
|
+
if (timestamp_start_ms_ && expires_after_ms_ && cas_ms > *timestamp_start_ms_) {
|
|
93
|
+
// Compute the effective TTL in std::uint64_t: expires_after_ms + safety_margin would
|
|
94
|
+
// otherwise be evaluated in std::uint32_t and could overflow for large TTLs/margins,
|
|
95
|
+
// flipping the comparison.
|
|
96
|
+
const std::uint64_t expires_after_ms = *expires_after_ms_;
|
|
77
97
|
return (cas_ms - *timestamp_start_ms_) > (expires_after_ms + safety_margin);
|
|
78
98
|
}
|
|
79
99
|
return false;
|
|
@@ -40,7 +40,7 @@ using txn_complete_callback =
|
|
|
40
40
|
std::function<void(std::optional<transaction_exception>,
|
|
41
41
|
std::optional<::couchbase::transactions::transaction_result>)>;
|
|
42
42
|
|
|
43
|
-
struct
|
|
43
|
+
struct async_timed_exp_delay;
|
|
44
44
|
|
|
45
45
|
class transaction_context : public std::enable_shared_from_this<transaction_context>
|
|
46
46
|
{
|
|
@@ -174,6 +174,6 @@ private:
|
|
|
174
174
|
std::shared_ptr<attempt_context_impl> current_attempt_context_;
|
|
175
175
|
mutable std::mutex mutex_;
|
|
176
176
|
|
|
177
|
-
std::unique_ptr<
|
|
177
|
+
std::unique_ptr<async_timed_exp_delay> delay_;
|
|
178
178
|
};
|
|
179
179
|
} // namespace couchbase::core::transactions
|
|
@@ -22,8 +22,12 @@
|
|
|
22
22
|
|
|
23
23
|
#include <couchbase/transactions/transactions_config.hxx>
|
|
24
24
|
|
|
25
|
+
#include <asio/thread_pool.hpp>
|
|
26
|
+
|
|
25
27
|
#include <atomic>
|
|
26
28
|
#include <condition_variable>
|
|
29
|
+
#include <cstdint>
|
|
30
|
+
#include <optional>
|
|
27
31
|
#include <thread>
|
|
28
32
|
|
|
29
33
|
namespace couchbase::core
|
|
@@ -118,7 +122,7 @@ public:
|
|
|
118
122
|
void add_collection(const couchbase::transactions::transaction_keyspace& keyspace);
|
|
119
123
|
auto collections() -> std::list<couchbase::transactions::transaction_keyspace>
|
|
120
124
|
{
|
|
121
|
-
std::
|
|
125
|
+
std::scoped_lock<std::mutex> lock(mutex_);
|
|
122
126
|
return collections_;
|
|
123
127
|
}
|
|
124
128
|
|
|
@@ -127,6 +131,11 @@ public:
|
|
|
127
131
|
// only used for testing
|
|
128
132
|
void force_cleanup_entry(atr_cleanup_entry& entry, transactions_cleanup_attempt& attempt);
|
|
129
133
|
// only used for testing
|
|
134
|
+
[[nodiscard]] auto client_uuid() const -> const std::string&
|
|
135
|
+
{
|
|
136
|
+
return client_uuid_;
|
|
137
|
+
}
|
|
138
|
+
// only used for testing
|
|
130
139
|
auto force_cleanup_atr(const core::document_id& atr_id,
|
|
131
140
|
std::vector<transactions_cleanup_attempt>& results) -> atr_cleanup_stats;
|
|
132
141
|
auto get_active_clients(const couchbase::transactions::transaction_keyspace& keyspace,
|
|
@@ -147,7 +156,20 @@ private:
|
|
|
147
156
|
mutable std::mutex mutex_;
|
|
148
157
|
std::list<std::thread> lost_attempt_cleanup_workers_;
|
|
149
158
|
|
|
159
|
+
// Bounded, shared pool of threads for the blocking per-ATR cleanup checks. These checks block on
|
|
160
|
+
// KV, so they must not run on the single Asio IO thread. The pool is shared across every
|
|
161
|
+
// collection cleanup worker, so this count bounds the TOTAL number of concurrent get_atr checks
|
|
162
|
+
// across the whole cleanup subsystem -- deliberately, to cap load on the cluster regardless of
|
|
163
|
+
// how many keyspaces are registered for cleanup. The same value doubles as each worker's
|
|
164
|
+
// in-flight cap: with one collection a worker saturates the pool; with many collections the
|
|
165
|
+
// shared pool multiplexes them (fair FIFO) and per-collection throughput drops accordingly, which
|
|
166
|
+
// is the intended trade-off (bounded cluster load over guaranteed per-collection pacing).
|
|
167
|
+
static constexpr std::size_t max_in_flight_atr_checks_{ 16 };
|
|
168
|
+
std::optional<asio::thread_pool> atr_cleanup_pool_;
|
|
169
|
+
|
|
150
170
|
const std::string client_uuid_;
|
|
171
|
+
// The process that registered client_uuid_. A forked child inherits the uuid but not the record.
|
|
172
|
+
const std::int64_t client_record_owner_pid_;
|
|
151
173
|
std::list<couchbase::transactions::transaction_keyspace> collections_;
|
|
152
174
|
|
|
153
175
|
void attempts_loop();
|
|
@@ -47,6 +47,25 @@ namespace transactions
|
|
|
47
47
|
std::uint64_t
|
|
48
48
|
now_ns_from_vbucket(const tao::json::value& vbucket);
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* ${Mutation.CAS} is written by kvengine with 'macroToString(htonll(info.cas))'. Discussed this
|
|
52
|
+
* with the KV team and, though there is consensus that this is off (htonll is definitely wrong,
|
|
53
|
+
* and a string is an odd choice), there are clients (SyncGateway) that consume the current
|
|
54
|
+
* string, so it can't be changed.
|
|
55
|
+
*
|
|
56
|
+
* The byte order inside that string is therefore fixed by the server, whatever host reads it,
|
|
57
|
+
* so recovering the value needs an unconditional reversal rather than a network-to-host
|
|
58
|
+
* conversion. See core::utils::reverse_bytes.
|
|
59
|
+
*
|
|
60
|
+
* Looks like: "0x000058a71dd25c15"
|
|
61
|
+
* Want: 0x155CD21DA7580000 (1539336197457313792 in base10, an epoch time in
|
|
62
|
+
* nanoseconds)
|
|
63
|
+
*
|
|
64
|
+
* @return epoch time in milliseconds, or 0 for an empty string
|
|
65
|
+
*/
|
|
66
|
+
std::uint64_t
|
|
67
|
+
parse_mutation_cas(const std::string& cas);
|
|
68
|
+
|
|
50
69
|
std::string
|
|
51
70
|
jsonify(const tao::json::value& obj);
|
|
52
71
|
|
|
@@ -175,7 +194,7 @@ jitter()
|
|
|
175
194
|
static std::mt19937 gen(rd());
|
|
176
195
|
static std::uniform_real_distribution<> dist(1 - RETRY_OP_JITTER, 1 + RETRY_OP_JITTER);
|
|
177
196
|
|
|
178
|
-
std::
|
|
197
|
+
std::scoped_lock<std::mutex> lock(mtx);
|
|
179
198
|
return dist(gen);
|
|
180
199
|
}
|
|
181
200
|
|
|
@@ -260,7 +279,13 @@ retry_op(std::function<R()> func)
|
|
|
260
279
|
DEFAULT_RETRY_OP_DELAY, std::numeric_limits<std::size_t>::max(), func);
|
|
261
280
|
}
|
|
262
281
|
|
|
263
|
-
|
|
282
|
+
// Exponential backoff bounded by a timeout, waiting on a timer so that it is safe to call from a
|
|
283
|
+
// completion handler. The first call starts the timeout and completes inline. Later calls complete
|
|
284
|
+
// after the backoff, or with retry_operation_timeout past the timeout or on cancellation, or with
|
|
285
|
+
// retry_operation_retries_exhausted after max_retries. Calls must not overlap: state is
|
|
286
|
+
// unsynchronised and expires_after cancels a pending wait.
|
|
287
|
+
struct async_timed_exp_delay {
|
|
288
|
+
std::shared_ptr<asio::steady_timer> timer;
|
|
264
289
|
std::chrono::nanoseconds initial_delay;
|
|
265
290
|
std::chrono::nanoseconds max_delay;
|
|
266
291
|
std::chrono::nanoseconds timeout;
|
|
@@ -269,36 +294,44 @@ struct exp_delay {
|
|
|
269
294
|
std::size_t max_retries{ 100 };
|
|
270
295
|
|
|
271
296
|
template<typename R1, typename P1, typename R2, typename P2, typename R3, typename P3>
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
297
|
+
async_timed_exp_delay(std::shared_ptr<asio::steady_timer> timer,
|
|
298
|
+
std::chrono::duration<R1, P1> initial,
|
|
299
|
+
std::chrono::duration<R2, P2> max,
|
|
300
|
+
std::chrono::duration<R3, P3> limit)
|
|
301
|
+
: timer(std::move(timer))
|
|
302
|
+
, initial_delay(std::chrono::duration_cast<std::chrono::nanoseconds>(initial))
|
|
276
303
|
, max_delay(std::chrono::duration_cast<std::chrono::nanoseconds>(max))
|
|
277
304
|
, timeout(std::chrono::duration_cast<std::chrono::nanoseconds>(limit))
|
|
278
305
|
{
|
|
279
306
|
}
|
|
280
|
-
void operator()() const
|
|
307
|
+
void operator()(utils::movable_function<void(std::exception_ptr)> callback) const
|
|
281
308
|
{
|
|
282
309
|
auto now = std::chrono::steady_clock::now();
|
|
283
310
|
if (retries >= max_retries) {
|
|
284
|
-
|
|
311
|
+
return callback(
|
|
312
|
+
std::make_exception_ptr(retry_operation_retries_exhausted("retries exhausted")));
|
|
285
313
|
}
|
|
286
314
|
if (!end_time) {
|
|
287
315
|
end_time = std::chrono::steady_clock::now() + timeout;
|
|
288
|
-
return;
|
|
316
|
+
return callback({});
|
|
289
317
|
}
|
|
290
318
|
if (now > *end_time) {
|
|
291
|
-
|
|
319
|
+
return callback(std::make_exception_ptr(retry_operation_timeout("timed out")));
|
|
292
320
|
}
|
|
293
321
|
auto delay = initial_delay * (jitter() * pow(2, retries++));
|
|
294
322
|
if (delay > max_delay) {
|
|
295
323
|
delay = max_delay;
|
|
296
324
|
}
|
|
297
325
|
if (now + delay > *end_time) {
|
|
298
|
-
|
|
299
|
-
} else {
|
|
300
|
-
std::this_thread::sleep_for(delay);
|
|
326
|
+
delay = *end_time - now;
|
|
301
327
|
}
|
|
328
|
+
timer->expires_after(std::chrono::duration_cast<std::chrono::nanoseconds>(delay));
|
|
329
|
+
timer->async_wait([callback = std::move(callback)](std::error_code ec) mutable {
|
|
330
|
+
if (ec == asio::error::operation_aborted) {
|
|
331
|
+
return callback(std::make_exception_ptr(retry_operation_timeout("retry aborted")));
|
|
332
|
+
}
|
|
333
|
+
callback({});
|
|
334
|
+
});
|
|
302
335
|
}
|
|
303
336
|
};
|
|
304
337
|
|
|
@@ -353,7 +386,10 @@ struct async_exp_delay {
|
|
|
353
386
|
|
|
354
387
|
void operator()(utils::movable_function<void(std::exception_ptr)> callback) const
|
|
355
388
|
{
|
|
356
|
-
|
|
389
|
+
// retries must be incremented exactly once per call: the guard reads it without mutating, and
|
|
390
|
+
// pow() does the single post-increment. Incrementing in both places double-counts -- halving
|
|
391
|
+
// the effective max_retries and doubling the exponent growth per attempt.
|
|
392
|
+
if (retries >= max_retries) {
|
|
357
393
|
callback(std::make_exception_ptr(retry_operation_retries_exhausted("retries exhausted")));
|
|
358
394
|
return;
|
|
359
395
|
}
|
|
@@ -110,7 +110,7 @@ staged_mutation::operation_id() const -> const std::string&
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
auto
|
|
113
|
-
staged_mutation::type_as_string() const -> std::
|
|
113
|
+
staged_mutation::type_as_string() const -> std::string_view
|
|
114
114
|
{
|
|
115
115
|
switch (type_) {
|
|
116
116
|
case staged_mutation_type::INSERT:
|
|
@@ -149,7 +149,7 @@ unstaging_state::wait_until_unstage_possible() -> bool
|
|
|
149
149
|
void
|
|
150
150
|
unstaging_state::notify_unstage_complete()
|
|
151
151
|
{
|
|
152
|
-
const std::
|
|
152
|
+
const std::scoped_lock lock(mutex_);
|
|
153
153
|
in_flight_count_--;
|
|
154
154
|
cv_.notify_one();
|
|
155
155
|
}
|
|
@@ -157,7 +157,7 @@ unstaging_state::notify_unstage_complete()
|
|
|
157
157
|
void
|
|
158
158
|
unstaging_state::notify_unstage_error()
|
|
159
159
|
{
|
|
160
|
-
const std::
|
|
160
|
+
const std::scoped_lock lock(mutex_);
|
|
161
161
|
abort_ = true;
|
|
162
162
|
in_flight_count_--;
|
|
163
163
|
cv_.notify_all();
|
|
@@ -166,14 +166,14 @@ unstaging_state::notify_unstage_error()
|
|
|
166
166
|
auto
|
|
167
167
|
staged_mutation_queue::empty() -> bool
|
|
168
168
|
{
|
|
169
|
-
const std::
|
|
169
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
170
170
|
return queue_.empty();
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
void
|
|
174
174
|
staged_mutation_queue::add(staged_mutation&& mutation)
|
|
175
175
|
{
|
|
176
|
-
const std::
|
|
176
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
177
177
|
// Can only have one staged mutation per document.
|
|
178
178
|
queue_.erase(std::remove_if(queue_.begin(),
|
|
179
179
|
queue_.end(),
|
|
@@ -188,16 +188,21 @@ void
|
|
|
188
188
|
staged_mutation_queue::extract_to(const std::string& prefix,
|
|
189
189
|
core::operations::mutate_in_request& req)
|
|
190
190
|
{
|
|
191
|
-
const std::
|
|
191
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
192
192
|
tao::json::value inserts = tao::json::empty_array;
|
|
193
193
|
tao::json::value replaces = tao::json::empty_array;
|
|
194
194
|
tao::json::value removes = tao::json::empty_array;
|
|
195
195
|
|
|
196
196
|
for (const auto& mutation : queue_) {
|
|
197
|
-
const tao::json::value doc{
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
const tao::json::value doc{
|
|
198
|
+
{ ATR_FIELD_PER_DOC_ID, mutation.id().key() },
|
|
199
|
+
{ ATR_FIELD_PER_DOC_BUCKET, mutation.id().bucket() },
|
|
200
|
+
{ ATR_FIELD_PER_DOC_SCOPE, mutation.id().scope() },
|
|
201
|
+
{
|
|
202
|
+
ATR_FIELD_PER_DOC_COLLECTION,
|
|
203
|
+
mutation.id().collection(),
|
|
204
|
+
},
|
|
205
|
+
};
|
|
201
206
|
switch (mutation.type()) {
|
|
202
207
|
case staged_mutation_type::INSERT:
|
|
203
208
|
inserts.push_back(doc);
|
|
@@ -232,7 +237,7 @@ staged_mutation_queue::extract_to(const std::string& prefix,
|
|
|
232
237
|
void
|
|
233
238
|
staged_mutation_queue::remove_any(const core::document_id& id)
|
|
234
239
|
{
|
|
235
|
-
const std::
|
|
240
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
236
241
|
auto new_end = std::remove_if(queue_.begin(), queue_.end(), [&id](const staged_mutation& item) {
|
|
237
242
|
return document_ids_equal(item.id(), id);
|
|
238
243
|
});
|
|
@@ -242,7 +247,7 @@ staged_mutation_queue::remove_any(const core::document_id& id)
|
|
|
242
247
|
auto
|
|
243
248
|
staged_mutation_queue::find_any(const core::document_id& id) -> staged_mutation*
|
|
244
249
|
{
|
|
245
|
-
const std::
|
|
250
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
246
251
|
for (auto& item : queue_) {
|
|
247
252
|
if (document_ids_equal(item.id(), id)) {
|
|
248
253
|
return &item;
|
|
@@ -254,7 +259,7 @@ staged_mutation_queue::find_any(const core::document_id& id) -> staged_mutation*
|
|
|
254
259
|
auto
|
|
255
260
|
staged_mutation_queue::find_replace(const core::document_id& id) -> staged_mutation*
|
|
256
261
|
{
|
|
257
|
-
const std::
|
|
262
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
258
263
|
for (auto& item : queue_) {
|
|
259
264
|
if (item.type() == staged_mutation_type::REPLACE && document_ids_equal(item.id(), id)) {
|
|
260
265
|
return &item;
|
|
@@ -266,7 +271,7 @@ staged_mutation_queue::find_replace(const core::document_id& id) -> staged_mutat
|
|
|
266
271
|
auto
|
|
267
272
|
staged_mutation_queue::find_insert(const core::document_id& id) -> staged_mutation*
|
|
268
273
|
{
|
|
269
|
-
const std::
|
|
274
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
270
275
|
for (auto& item : queue_) {
|
|
271
276
|
if (item.type() == staged_mutation_type::INSERT && document_ids_equal(item.id(), id)) {
|
|
272
277
|
return &item;
|
|
@@ -278,7 +283,7 @@ staged_mutation_queue::find_insert(const core::document_id& id) -> staged_mutati
|
|
|
278
283
|
auto
|
|
279
284
|
staged_mutation_queue::find_remove(const core::document_id& id) -> staged_mutation*
|
|
280
285
|
{
|
|
281
|
-
const std::
|
|
286
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
282
287
|
for (auto& item : queue_) {
|
|
283
288
|
if (item.type() == staged_mutation_type::REMOVE && document_ids_equal(item.id(), id)) {
|
|
284
289
|
return &item;
|
|
@@ -289,7 +294,7 @@ staged_mutation_queue::find_remove(const core::document_id& id) -> staged_mutati
|
|
|
289
294
|
void
|
|
290
295
|
staged_mutation_queue::iterate(const std::function<void(staged_mutation&)>& op)
|
|
291
296
|
{
|
|
292
|
-
const std::
|
|
297
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
293
298
|
for (auto& item : queue_) {
|
|
294
299
|
op(item);
|
|
295
300
|
}
|
|
@@ -299,7 +304,7 @@ void
|
|
|
299
304
|
staged_mutation_queue::commit(const std::shared_ptr<attempt_context_impl>& ctx)
|
|
300
305
|
{
|
|
301
306
|
CB_ATTEMPT_CTX_LOG_TRACE(ctx, "committing staged mutations...");
|
|
302
|
-
const std::
|
|
307
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
303
308
|
|
|
304
309
|
unstaging_state state{ ctx };
|
|
305
310
|
std::vector<std::future<void>> futures{};
|
|
@@ -390,7 +395,7 @@ void
|
|
|
390
395
|
staged_mutation_queue::rollback(const std::shared_ptr<attempt_context_impl>& ctx)
|
|
391
396
|
{
|
|
392
397
|
CB_ATTEMPT_CTX_LOG_TRACE(ctx, "rolling back staged mutations...");
|
|
393
|
-
const std::
|
|
398
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
394
399
|
|
|
395
400
|
unstaging_state state{ ctx };
|
|
396
401
|
std::vector<std::future<void>> futures{};
|
|
@@ -23,8 +23,12 @@
|
|
|
23
23
|
|
|
24
24
|
#include <couchbase/codec/encoded_value.hxx>
|
|
25
25
|
|
|
26
|
+
#include <atomic>
|
|
27
|
+
#include <condition_variable>
|
|
26
28
|
#include <mutex>
|
|
29
|
+
#include <optional>
|
|
27
30
|
#include <string>
|
|
31
|
+
#include <string_view>
|
|
28
32
|
#include <vector>
|
|
29
33
|
|
|
30
34
|
namespace couchbase::core::transactions
|
|
@@ -32,7 +36,7 @@ namespace couchbase::core::transactions
|
|
|
32
36
|
enum class staged_mutation_type {
|
|
33
37
|
INSERT,
|
|
34
38
|
REMOVE,
|
|
35
|
-
REPLACE
|
|
39
|
+
REPLACE,
|
|
36
40
|
};
|
|
37
41
|
|
|
38
42
|
class staged_mutation
|
|
@@ -62,7 +66,14 @@ public:
|
|
|
62
66
|
[[nodiscard]] auto current_user_flags() const -> std::uint32_t;
|
|
63
67
|
[[nodiscard]] auto doc_metadata() const -> const std::optional<document_metadata>&;
|
|
64
68
|
[[nodiscard]] auto operation_id() const -> const std::string&;
|
|
65
|
-
|
|
69
|
+
/**
|
|
70
|
+
* @brief Returns the human-readable name of the mutation type as a string literal view.
|
|
71
|
+
*
|
|
72
|
+
* Returns one of @c "INSERT", @c "REPLACE", or @c "REMOVE" without any heap allocation.
|
|
73
|
+
* The returned view is backed by a string literal and remains valid for the lifetime of the
|
|
74
|
+
* program.
|
|
75
|
+
*/
|
|
76
|
+
[[nodiscard]] auto type_as_string() const -> std::string_view;
|
|
66
77
|
|
|
67
78
|
void cas(couchbase::cas cas);
|
|
68
79
|
|
|
@@ -166,8 +177,8 @@ public:
|
|
|
166
177
|
void extract_to(const std::string& prefix, core::operations::mutate_in_request& req);
|
|
167
178
|
void commit(const std::shared_ptr<attempt_context_impl>& ctx);
|
|
168
179
|
void rollback(const std::shared_ptr<attempt_context_impl>& ctx);
|
|
169
|
-
void iterate(const std::function<void(staged_mutation&)>&);
|
|
170
|
-
void remove_any(const core::document_id&);
|
|
180
|
+
void iterate(const std::function<void(staged_mutation& /*mutation*/)>&);
|
|
181
|
+
void remove_any(const core::document_id& /*id*/);
|
|
171
182
|
|
|
172
183
|
auto find_any(const core::document_id& id) -> staged_mutation*;
|
|
173
184
|
auto find_replace(const core::document_id& id) -> staged_mutation*;
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
#include <asio/post.hpp>
|
|
29
29
|
#include <asio/steady_timer.hpp>
|
|
30
|
+
#include <optional>
|
|
30
31
|
#include <utility>
|
|
31
32
|
|
|
32
33
|
namespace couchbase::core::transactions
|
|
@@ -59,9 +60,11 @@ transaction_context::transaction_context(transactions& txns,
|
|
|
59
60
|
, config_(config.apply(txns.config()))
|
|
60
61
|
, deferred_elapsed_(0)
|
|
61
62
|
, cleanup_(txns.cleanup())
|
|
62
|
-
, delay_(
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
, delay_(std::make_unique<async_timed_exp_delay>(
|
|
64
|
+
std::make_shared<asio::steady_timer>(txns.cluster_ref().io_context()),
|
|
65
|
+
std::chrono::milliseconds(1),
|
|
66
|
+
std::chrono::milliseconds(100),
|
|
67
|
+
2 * config_.timeout))
|
|
65
68
|
{
|
|
66
69
|
// add metadata_collection to cleanup, if present
|
|
67
70
|
if (config_.metadata_collection) {
|
|
@@ -77,7 +80,7 @@ void
|
|
|
77
80
|
transaction_context::add_attempt()
|
|
78
81
|
{
|
|
79
82
|
const transaction_attempt attempt{};
|
|
80
|
-
const std::
|
|
83
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
81
84
|
attempts_.push_back(attempt);
|
|
82
85
|
}
|
|
83
86
|
|
|
@@ -138,21 +141,25 @@ void
|
|
|
138
141
|
transaction_context::new_attempt_context(async_attempt_context::VoidCallback&& cb)
|
|
139
142
|
{
|
|
140
143
|
asio::post(transactions_.cluster_ref().io_context(),
|
|
141
|
-
[self = shared_from_this(), cb = std::move(cb)]() {
|
|
144
|
+
[self = shared_from_this(), cb = std::move(cb)]() mutable {
|
|
142
145
|
// the first time we call the delay, it just records an end time. After
|
|
143
146
|
// that, it actually delays.
|
|
144
|
-
|
|
145
|
-
(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
147
|
+
(*self->delay_)([self, cb = std::move(cb)](const std::exception_ptr& err) mutable {
|
|
148
|
+
if (err) {
|
|
149
|
+
return cb(err);
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
self->current_attempt_context_ = attempt_context_impl::create(self);
|
|
153
|
+
CB_ATTEMPT_CTX_LOG_INFO(self->current_attempt_context_,
|
|
154
|
+
"starting attempt {}/{}/{}/",
|
|
155
|
+
self->num_attempts(),
|
|
156
|
+
self->transaction_id(),
|
|
157
|
+
self->current_attempt_context_->id());
|
|
158
|
+
cb(nullptr);
|
|
159
|
+
} catch (...) {
|
|
160
|
+
cb(std::current_exception());
|
|
161
|
+
}
|
|
162
|
+
});
|
|
156
163
|
});
|
|
157
164
|
}
|
|
158
165
|
|
|
@@ -299,6 +306,18 @@ transaction_context::existing_error(bool previous_op_failed)
|
|
|
299
306
|
void
|
|
300
307
|
transaction_context::handle_error(const std::exception_ptr& err, txn_complete_callback&& callback)
|
|
301
308
|
{
|
|
309
|
+
auto auto_rollback = [this]() -> std::optional<final_error> {
|
|
310
|
+
try {
|
|
311
|
+
current_attempt_context_->rollback();
|
|
312
|
+
return {};
|
|
313
|
+
} catch (const std::exception& e) {
|
|
314
|
+
CB_ATTEMPT_CTX_LOG_ERROR(current_attempt_context_, "auto rollback failed: {}", e.what());
|
|
315
|
+
} catch (...) {
|
|
316
|
+
CB_ATTEMPT_CTX_LOG_ERROR(current_attempt_context_, "auto rollback failed");
|
|
317
|
+
}
|
|
318
|
+
return current_attempt_context_->query_check_expired_ ? EXPIRED : FAILED;
|
|
319
|
+
};
|
|
320
|
+
|
|
302
321
|
try {
|
|
303
322
|
try {
|
|
304
323
|
std::rethrow_exception(err);
|
|
@@ -316,16 +335,15 @@ transaction_context::handle_error(const std::exception_ptr& err, txn_complete_ca
|
|
|
316
335
|
if (er.should_rollback()) {
|
|
317
336
|
CB_ATTEMPT_CTX_LOG_TRACE(current_attempt_context_,
|
|
318
337
|
"got rollback-able exception, rolling back");
|
|
319
|
-
|
|
320
|
-
current_attempt_context_->rollback();
|
|
321
|
-
} catch (const std::exception& er_rollback) {
|
|
338
|
+
if (auto rollback_failed = auto_rollback(); rollback_failed) {
|
|
322
339
|
cleanup().add_attempt(current_attempt_context_);
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
340
|
+
// A failed auto-rollback raises the original error without retry, or EXPIRED in place of
|
|
341
|
+
// FAILED.
|
|
342
|
+
auto raised = er;
|
|
343
|
+
if (rollback_failed == EXPIRED && raised.to_raise() == FAILED) {
|
|
344
|
+
raised.expired();
|
|
345
|
+
}
|
|
346
|
+
auto final = raised.get_final_exception(*this);
|
|
329
347
|
// if you get here, we didn't throw, yet we had an error. Fall through
|
|
330
348
|
// in this case. Note the current logic is such that rollback will not
|
|
331
349
|
// have a commit ambiguous error, so we should always throw.
|
|
@@ -359,28 +377,25 @@ transaction_context::handle_error(const std::exception_ptr& err, txn_complete_ca
|
|
|
359
377
|
return callback(final, res);
|
|
360
378
|
} catch (const std::exception& ex) {
|
|
361
379
|
CB_ATTEMPT_CTX_LOG_ERROR(current_attempt_context_, "got runtime error \"{}\"", ex.what());
|
|
362
|
-
|
|
363
|
-
current_attempt_context_->rollback();
|
|
364
|
-
} catch (...) {
|
|
365
|
-
CB_ATTEMPT_CTX_LOG_ERROR(
|
|
366
|
-
current_attempt_context_, "got error rolling back \"{}\"", ex.what());
|
|
367
|
-
}
|
|
380
|
+
const auto rollback_failed = auto_rollback();
|
|
368
381
|
cleanup().add_attempt(current_attempt_context_);
|
|
369
382
|
// the assumption here is this must come from the logic, not
|
|
370
383
|
// our operations (which only throw transaction_operation_failed),
|
|
371
384
|
auto op_failed = transaction_operation_failed(FAIL_OTHER, ex.what());
|
|
385
|
+
if (rollback_failed == EXPIRED) {
|
|
386
|
+
op_failed.expired();
|
|
387
|
+
}
|
|
372
388
|
return callback(op_failed.get_final_exception(*this), std::nullopt);
|
|
373
389
|
} catch (...) {
|
|
374
390
|
CB_ATTEMPT_CTX_LOG_ERROR(current_attempt_context_, "got unexpected error, rolling back");
|
|
375
|
-
|
|
376
|
-
current_attempt_context_->rollback();
|
|
377
|
-
} catch (...) {
|
|
378
|
-
CB_ATTEMPT_CTX_LOG_ERROR(current_attempt_context_, "got error rolling back unexpected error");
|
|
379
|
-
}
|
|
391
|
+
const auto rollback_failed = auto_rollback();
|
|
380
392
|
cleanup().add_attempt(current_attempt_context_);
|
|
381
393
|
// the assumption here is this must come from the logic, not
|
|
382
394
|
// our operations (which only throw transaction_operation_failed),
|
|
383
395
|
auto op_failed = transaction_operation_failed(FAIL_OTHER, "Unexpected error");
|
|
396
|
+
if (rollback_failed == EXPIRED) {
|
|
397
|
+
op_failed.expired();
|
|
398
|
+
}
|
|
384
399
|
return callback(op_failed.get_final_exception(*this), std::nullopt);
|
|
385
400
|
}
|
|
386
401
|
}
|
|
@@ -408,7 +423,7 @@ transaction_context::finalize(txn_complete_callback&& cb)
|
|
|
408
423
|
void
|
|
409
424
|
transaction_context::current_attempt_state(attempt_state s)
|
|
410
425
|
{
|
|
411
|
-
const std::
|
|
426
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
412
427
|
if (attempts_.empty()) {
|
|
413
428
|
throw std::runtime_error("transaction_context has no attempts yet");
|
|
414
429
|
}
|
|
@@ -488,7 +503,7 @@ transaction_context::new_attempt_context()
|
|
|
488
503
|
auto
|
|
489
504
|
transaction_context::current_attempt() const -> const transaction_attempt&
|
|
490
505
|
{
|
|
491
|
-
const std::
|
|
506
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
492
507
|
if (attempts_.empty()) {
|
|
493
508
|
throw std::runtime_error("transaction context has no attempts yet");
|
|
494
509
|
}
|
|
@@ -498,7 +513,7 @@ transaction_context::current_attempt() const -> const transaction_attempt&
|
|
|
498
513
|
auto
|
|
499
514
|
transaction_context::num_attempts() const -> std::size_t
|
|
500
515
|
{
|
|
501
|
-
const std::
|
|
516
|
+
const std::scoped_lock<std::mutex> lock(mutex_);
|
|
502
517
|
return attempts_.size();
|
|
503
518
|
}
|
|
504
519
|
|
|
@@ -23,9 +23,11 @@
|
|
|
23
23
|
#include "transaction_links.hxx"
|
|
24
24
|
|
|
25
25
|
#include <couchbase/codec/encoded_value.hxx>
|
|
26
|
+
#include <couchbase/error_codes.hxx>
|
|
26
27
|
#include <couchbase/fmt/cas.hxx>
|
|
27
28
|
#include <couchbase/transactions/transaction_get_result.hxx>
|
|
28
29
|
|
|
30
|
+
#include <system_error>
|
|
29
31
|
#include <utility>
|
|
30
32
|
|
|
31
33
|
namespace couchbase::core::transactions
|
|
@@ -108,6 +110,20 @@ public:
|
|
|
108
110
|
cas != nullptr && cas->is_string() && cas_.value() == 0U) {
|
|
109
111
|
cas_ = couchbase::cas(stoull(cas->as<std::string>()));
|
|
110
112
|
}
|
|
113
|
+
// This ctor parses the JSON row returned by a query-mode KV get/insert/replace,
|
|
114
|
+
// where the document CAS is carried as the string field "scas" (a numeric
|
|
115
|
+
// "cas" is also accepted above). Per the transactions query spec, KV
|
|
116
|
+
// get/insert/replace result handling requires scas to be present and raises a
|
|
117
|
+
// DecodingFailure when it is absent: without a usable CAS, cas_ stays at its
|
|
118
|
+
// default 0, which is not a valid document CAS and would corrupt a later COMMIT
|
|
119
|
+
// that stages against it. The query KV op handlers translate this
|
|
120
|
+
// DecodingFailure into a FAIL_OTHER (rollback). "doc" is intentionally not
|
|
121
|
+
// required here, since insert/replace rows and content-less reads legitimately
|
|
122
|
+
// carry a CAS but no content.
|
|
123
|
+
if (cas_.value() == 0U) {
|
|
124
|
+
throw std::system_error(couchbase::errc::common::decoding_failure,
|
|
125
|
+
"transaction get result row is missing a usable CAS (scas or cas)");
|
|
126
|
+
}
|
|
111
127
|
if (const auto* doc = json.find("doc"); doc != nullptr) {
|
|
112
128
|
content_ = { core::utils::json::generate_binary(doc->get_object()),
|
|
113
129
|
codec::codec_flags::json_common_flags };
|