couchbase 3.8.2 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (473) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/ext/CMakeLists.txt +16 -1
  4. data/ext/cache/cpm/{CPM_0.42.0.cmake → CPM_0.43.1.cmake} +18 -2
  5. data/ext/cache/extconf_include.rb +6 -6
  6. data/ext/cache/mozilla-ca-bundle.crt +121 -55
  7. data/ext/cache/mozilla-ca-bundle.sha256 +1 -1
  8. data/ext/couchbase/CMakeLists.txt +130 -21
  9. data/ext/couchbase/cmake/APKBUILD.in +25 -4
  10. data/ext/couchbase/cmake/Bundler.cmake +143 -34
  11. data/ext/couchbase/cmake/CPM.cmake +126 -12
  12. data/ext/couchbase/cmake/Cache.cmake +7 -1
  13. data/ext/couchbase/cmake/CompilerOptions.cmake +1 -1
  14. data/ext/couchbase/cmake/CompilerWarnings.cmake +17 -6
  15. data/ext/couchbase/cmake/GrpcProtobuf.cmake +481 -0
  16. data/ext/couchbase/cmake/OpenSSL.cmake +10 -3
  17. data/ext/couchbase/cmake/Packaging.cmake +519 -65
  18. data/ext/couchbase/cmake/Protostellar.cmake +250 -0
  19. data/ext/couchbase/cmake/Sanitizers.cmake +61 -50
  20. data/ext/couchbase/cmake/StaticAnalyzers.cmake +16 -5
  21. data/ext/couchbase/cmake/TestFramework.cmake +283 -0
  22. data/ext/couchbase/cmake/TestFrameworkAddTests.cmake +51 -0
  23. data/ext/couchbase/cmake/Testing.cmake +27 -9
  24. data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +141 -8
  25. data/ext/couchbase/cmake/VersionInfo.cmake +50 -0
  26. data/ext/couchbase/cmake/build_config.hxx.in +2 -1
  27. data/ext/couchbase/cmake/couchbase-cxx-client.spec.in +101 -23
  28. data/ext/couchbase/cmake/couchbase_cxx_client_static-config.cmake.in +7 -0
  29. data/ext/couchbase/cmake/couchbase_cxx_client_static.pc.in +2 -0
  30. data/ext/couchbase/cmake/gRPC_Generate.cmake +58 -0
  31. data/ext/couchbase/cmake/protobuf_Generate.cmake +69 -0
  32. data/ext/couchbase/cmake/require-git-checkout.cmake +25 -0
  33. data/ext/couchbase/cmake/tarball_glob.txt +49 -0
  34. data/ext/couchbase/core/agent_group.cxx +0 -8
  35. data/ext/couchbase/core/agent_group.hxx +0 -4
  36. data/ext/couchbase/core/analytics_stream.cxx +314 -0
  37. data/ext/couchbase/core/analytics_stream.hxx +129 -0
  38. data/ext/couchbase/core/analytics_stream_component.cxx +245 -0
  39. data/ext/couchbase/core/analytics_stream_component.hxx +67 -0
  40. data/ext/couchbase/core/app_telemetry_meter.cxx +25 -3
  41. data/ext/couchbase/core/app_telemetry_meter.hxx +77 -0
  42. data/ext/couchbase/core/app_telemetry_reporter.cxx +7 -6
  43. data/ext/couchbase/core/bucket.cxx +381 -87
  44. data/ext/couchbase/core/bucket.hxx +105 -25
  45. data/ext/couchbase/core/bucket_unit_test_api.hxx +57 -0
  46. data/ext/couchbase/core/capella.hxx +91 -0
  47. data/ext/couchbase/core/cluster.cxx +1554 -398
  48. data/ext/couchbase/core/cluster.hxx +39 -8
  49. data/ext/couchbase/core/cluster_agent.cxx +0 -8
  50. data/ext/couchbase/core/cluster_agent.hxx +0 -4
  51. data/ext/couchbase/core/cluster_options.hxx +26 -3
  52. data/ext/couchbase/core/collections_component.cxx +39 -15
  53. data/ext/couchbase/core/config_profiles.cxx +1 -1
  54. data/ext/couchbase/core/crud_component.cxx +2 -2
  55. data/ext/couchbase/core/crud_options.hxx +34 -0
  56. data/ext/couchbase/core/crypto/cbcrypto.cc +18 -13
  57. data/ext/couchbase/core/document_id_redaction.hxx +81 -0
  58. data/ext/couchbase/core/error_context/base_error_context.hxx +7 -6
  59. data/ext/couchbase/core/error_context/key_value.cxx +25 -2
  60. data/ext/couchbase/core/error_context/key_value.hxx +60 -33
  61. data/ext/couchbase/core/error_context/key_value_error_context.hxx +16 -15
  62. data/ext/couchbase/core/error_context/search.hxx +5 -0
  63. data/ext/couchbase/core/error_context/search_json.hxx +3 -0
  64. data/ext/couchbase/core/error_context/subdocument_error_context.hxx +19 -18
  65. data/ext/couchbase/core/error_context/view.hxx +5 -0
  66. data/ext/couchbase/core/free_form_http_request.cxx +237 -4
  67. data/ext/couchbase/core/free_form_http_request.hxx +41 -8
  68. data/ext/couchbase/core/http_component.cxx +300 -284
  69. data/ext/couchbase/core/http_component.hxx +0 -4
  70. data/ext/couchbase/core/impl/analytics.cxx +38 -5
  71. data/ext/couchbase/core/impl/analytics.hxx +34 -0
  72. data/ext/couchbase/core/impl/bootstrap_error.hxx +0 -3
  73. data/ext/couchbase/core/impl/collection.cxx +215 -202
  74. data/ext/couchbase/core/impl/collection_manager.cxx +1 -1
  75. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +45 -18
  76. data/ext/couchbase/core/impl/error.cxx +22 -2
  77. data/ext/couchbase/core/impl/internal_analytics_stream_result.hxx +111 -0
  78. data/ext/couchbase/core/impl/internal_query_stream_result.hxx +110 -0
  79. data/ext/couchbase/core/impl/invoke_with_node_id.hxx +15 -7
  80. data/ext/couchbase/core/impl/key_value_error_category.cxx +6 -0
  81. data/ext/couchbase/core/impl/lookup_in_replica.cxx +10 -10
  82. data/ext/couchbase/core/impl/lookup_in_replica.hxx +4 -4
  83. data/ext/couchbase/core/impl/node_id.cxx +49 -13
  84. data/ext/couchbase/core/impl/observability_recorder.cxx +12 -2
  85. data/ext/couchbase/core/impl/observe_poll.cxx +63 -23
  86. data/ext/couchbase/core/impl/public_analytics_stream_result.cxx +372 -0
  87. data/ext/couchbase/core/impl/public_bucket.cxx +48 -2
  88. data/ext/couchbase/core/impl/public_cluster.cxx +229 -42
  89. data/ext/couchbase/core/impl/public_query_stream_result.cxx +380 -0
  90. data/ext/couchbase/core/impl/public_transaction_get_multi_replicas_from_preferred_server_group_result.cxx +42 -0
  91. data/ext/couchbase/core/impl/public_transaction_get_multi_result.cxx +42 -0
  92. data/ext/couchbase/core/impl/query.cxx +76 -7
  93. data/ext/couchbase/core/impl/query.hxx +36 -0
  94. data/ext/couchbase/core/impl/query_index_manager.cxx +54 -1
  95. data/ext/couchbase/core/impl/replica_utils.cxx +101 -0
  96. data/ext/couchbase/core/impl/replica_utils.hxx +64 -0
  97. data/ext/couchbase/core/impl/resolve_node_id.hxx +125 -0
  98. data/ext/couchbase/core/impl/scope.cxx +70 -0
  99. data/ext/couchbase/core/impl/search.cxx +31 -1
  100. data/ext/couchbase/core/impl/transactions.cxx +130 -0
  101. data/ext/couchbase/core/impl/wait_until_ready.cxx +439 -0
  102. data/ext/couchbase/core/impl/wait_until_ready.hxx +110 -0
  103. data/ext/couchbase/core/impl/with_bucket_config_or_timeout.hxx +180 -0
  104. data/ext/couchbase/core/impl/with_cancellation.hxx +11 -3
  105. data/ext/couchbase/core/io/configuration_belongs_to_session.hxx +1 -2
  106. data/ext/couchbase/core/io/dns_client.cxx +122 -45
  107. data/ext/couchbase/core/io/dns_codec.hxx +21 -21
  108. data/ext/couchbase/core/io/dns_config.cxx +21 -6
  109. data/ext/couchbase/core/io/http_command.hxx +149 -121
  110. data/ext/couchbase/core/io/http_error.hxx +107 -0
  111. data/ext/couchbase/core/io/http_parser.cxx +4 -0
  112. data/ext/couchbase/core/io/http_session.cxx +606 -154
  113. data/ext/couchbase/core/io/http_session.hxx +147 -30
  114. data/ext/couchbase/core/io/http_session_manager.hxx +329 -382
  115. data/ext/couchbase/core/io/http_streaming_parser.cxx +4 -0
  116. data/ext/couchbase/core/io/http_streaming_response.cxx +328 -43
  117. data/ext/couchbase/core/io/http_streaming_response.hxx +37 -3
  118. data/ext/couchbase/core/io/mcbp_buffer_pool.hxx +122 -0
  119. data/ext/couchbase/core/io/mcbp_command.hxx +150 -51
  120. data/ext/couchbase/core/io/mcbp_command_id.hxx +45 -0
  121. data/ext/couchbase/core/io/mcbp_message.cxx +9 -1
  122. data/ext/couchbase/core/io/mcbp_message.hxx +19 -0
  123. data/ext/couchbase/core/io/mcbp_output_queue.hxx +150 -0
  124. data/ext/couchbase/core/io/mcbp_parser.cxx +24 -7
  125. data/ext/couchbase/core/io/mcbp_session.cxx +309 -301
  126. data/ext/couchbase/core/io/mcbp_session.hxx +27 -18
  127. data/ext/couchbase/core/io/opaque_ring_table.hxx +145 -0
  128. data/ext/couchbase/core/io/retry_orchestrator.hxx +6 -5
  129. data/ext/couchbase/core/io/stream_deadline.hxx +50 -0
  130. data/ext/couchbase/core/io/streams.cxx +151 -21
  131. data/ext/couchbase/core/io/streams.hxx +48 -1
  132. data/ext/couchbase/core/logger/logger.cxx +36 -0
  133. data/ext/couchbase/core/logger/logger.hxx +46 -6
  134. data/ext/couchbase/core/logger/redaction.hxx +503 -0
  135. data/ext/couchbase/core/management/eventing_status.hxx +10 -0
  136. data/ext/couchbase/core/management/eventing_status_json.hxx +4 -0
  137. data/ext/couchbase/core/mcbp/big_endian.cxx +1 -1
  138. data/ext/couchbase/core/mcbp/codec.cxx +27 -18
  139. data/ext/couchbase/core/mcbp/operation_queue.cxx +2 -2
  140. data/ext/couchbase/core/mcbp/packet.hxx +2 -2
  141. data/ext/couchbase/core/mcbp/queue_request.cxx +23 -6
  142. data/ext/couchbase/core/mcbp/queue_request.hxx +21 -0
  143. data/ext/couchbase/core/meta/features.hxx +13 -0
  144. data/ext/couchbase/core/meta/version.cxx +6 -3
  145. data/ext/couchbase/core/metrics/logging_meter.cxx +5 -10
  146. data/ext/couchbase/core/metrics/meter_wrapper.cxx +63 -33
  147. data/ext/couchbase/core/metrics/meter_wrapper.hxx +55 -3
  148. data/ext/couchbase/core/operations/analytics_response_parsing.cxx +177 -0
  149. data/ext/couchbase/core/operations/analytics_response_parsing.hxx +55 -0
  150. data/ext/couchbase/core/operations/document_analytics.cxx +48 -130
  151. data/ext/couchbase/core/operations/document_analytics.hxx +14 -2
  152. data/ext/couchbase/core/operations/document_get_all_replicas.hxx +15 -7
  153. data/ext/couchbase/core/operations/document_get_any_replica.hxx +19 -8
  154. data/ext/couchbase/core/operations/document_get_projected.cxx +1 -1
  155. data/ext/couchbase/core/{impl/get_replica.cxx → operations/document_get_replica.cxx} +21 -3
  156. data/ext/couchbase/core/{impl/get_replica.hxx → operations/document_get_replica.hxx} +31 -4
  157. data/ext/couchbase/core/operations/document_lookup_in.cxx +10 -10
  158. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +228 -198
  159. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +215 -217
  160. data/ext/couchbase/core/operations/document_query.cxx +108 -221
  161. data/ext/couchbase/core/operations/document_query.hxx +18 -4
  162. data/ext/couchbase/core/operations/document_search.cxx +52 -8
  163. data/ext/couchbase/core/operations/document_search.hxx +3 -0
  164. data/ext/couchbase/core/operations/document_view.cxx +3 -3
  165. data/ext/couchbase/core/operations/management/analytics_dataset_create.cxx +15 -8
  166. data/ext/couchbase/core/operations/management/analytics_dataset_create.hxx +4 -0
  167. data/ext/couchbase/core/operations/management/analytics_dataset_drop.cxx +11 -6
  168. data/ext/couchbase/core/operations/management/analytics_dataset_get_all.cxx +3 -2
  169. data/ext/couchbase/core/operations/management/analytics_dataverse_create.cxx +10 -5
  170. data/ext/couchbase/core/operations/management/analytics_dataverse_drop.cxx +10 -5
  171. data/ext/couchbase/core/operations/management/analytics_get_pending_mutations.cxx +2 -1
  172. data/ext/couchbase/core/operations/management/analytics_index_create.cxx +33 -8
  173. data/ext/couchbase/core/operations/management/analytics_index_drop.cxx +12 -7
  174. data/ext/couchbase/core/operations/management/analytics_index_get_all.cxx +3 -2
  175. data/ext/couchbase/core/operations/management/analytics_link_connect.cxx +11 -6
  176. data/ext/couchbase/core/operations/management/analytics_link_create.cxx +3 -3
  177. data/ext/couchbase/core/operations/management/analytics_link_disconnect.cxx +10 -4
  178. data/ext/couchbase/core/operations/management/analytics_link_drop.cxx +6 -4
  179. data/ext/couchbase/core/operations/management/analytics_link_get_all.cxx +4 -3
  180. data/ext/couchbase/core/operations/management/analytics_link_replace.cxx +3 -3
  181. data/ext/couchbase/core/operations/management/analytics_link_utils.hxx +1 -1
  182. data/ext/couchbase/core/operations/management/change_password.cxx +2 -0
  183. data/ext/couchbase/core/operations/management/collection_drop.cxx +5 -1
  184. data/ext/couchbase/core/operations/management/error_utils.cxx +59 -5
  185. data/ext/couchbase/core/operations/management/error_utils.hxx +29 -1
  186. data/ext/couchbase/core/operations/management/eventing_deploy_function.cxx +9 -3
  187. data/ext/couchbase/core/operations/management/eventing_drop_function.cxx +8 -3
  188. data/ext/couchbase/core/operations/management/eventing_get_all_functions.cxx +4 -2
  189. data/ext/couchbase/core/operations/management/eventing_get_function.cxx +5 -3
  190. data/ext/couchbase/core/operations/management/eventing_get_status.cxx +4 -2
  191. data/ext/couchbase/core/operations/management/eventing_pause_function.cxx +9 -3
  192. data/ext/couchbase/core/operations/management/eventing_resume_function.cxx +9 -3
  193. data/ext/couchbase/core/operations/management/eventing_undeploy_function.cxx +9 -3
  194. data/ext/couchbase/core/operations/management/eventing_upsert_function.cxx +9 -3
  195. data/ext/couchbase/core/operations/management/group_drop.cxx +3 -1
  196. data/ext/couchbase/core/operations/management/group_get.cxx +3 -1
  197. data/ext/couchbase/core/operations/management/group_upsert.cxx +11 -8
  198. data/ext/couchbase/core/operations/management/query_index_build.cxx +5 -2
  199. data/ext/couchbase/core/operations/management/query_index_create.cxx +9 -3
  200. data/ext/couchbase/core/operations/management/query_index_drop.cxx +11 -4
  201. data/ext/couchbase/core/operations/management/query_index_get_all.cxx +1 -1
  202. data/ext/couchbase/core/operations/management/query_index_get_all_deferred.cxx +1 -1
  203. data/ext/couchbase/core/operations/management/scope_drop.cxx +5 -1
  204. data/ext/couchbase/core/operations/management/scope_get_all.cxx +6 -1
  205. data/ext/couchbase/core/operations/management/search_get_stats.cxx +10 -0
  206. data/ext/couchbase/core/operations/management/search_index_analyze_document.cxx +3 -2
  207. data/ext/couchbase/core/operations/management/search_index_control_ingest.cxx +4 -3
  208. data/ext/couchbase/core/operations/management/search_index_control_plan_freeze.cxx +4 -3
  209. data/ext/couchbase/core/operations/management/search_index_control_query.cxx +4 -3
  210. data/ext/couchbase/core/operations/management/search_index_drop.cxx +2 -2
  211. data/ext/couchbase/core/operations/management/search_index_get.cxx +2 -2
  212. data/ext/couchbase/core/operations/management/search_index_get_documents_count.cxx +3 -2
  213. data/ext/couchbase/core/operations/management/search_index_get_stats.cxx +3 -1
  214. data/ext/couchbase/core/operations/management/search_index_upsert.cxx +2 -2
  215. data/ext/couchbase/core/operations/management/user_drop.cxx +3 -1
  216. data/ext/couchbase/core/operations/management/user_get.cxx +3 -1
  217. data/ext/couchbase/core/operations/management/user_upsert.cxx +12 -9
  218. data/ext/couchbase/core/operations/management/view_index_drop.cxx +1 -1
  219. data/ext/couchbase/core/operations/management/view_index_get.cxx +1 -1
  220. data/ext/couchbase/core/operations/management/view_index_upsert.cxx +1 -1
  221. data/ext/couchbase/core/operations/operation_traits.hxx +18 -0
  222. data/ext/couchbase/core/operations/query_response_parsing.cxx +211 -0
  223. data/ext/couchbase/core/operations/query_response_parsing.hxx +60 -0
  224. data/ext/couchbase/core/operations_fwd.hxx +3 -3
  225. data/ext/couchbase/core/origin.cxx +54 -26
  226. data/ext/couchbase/core/origin.hxx +12 -0
  227. data/ext/couchbase/core/orphan_reporter.cxx +2 -1
  228. data/ext/couchbase/core/platform/random.cc +7 -7
  229. data/ext/couchbase/core/protocol/client_request.cxx +2 -2
  230. data/ext/couchbase/core/protocol/client_request.hxx +38 -21
  231. data/ext/couchbase/core/protocol/client_response.cxx +11 -3
  232. data/ext/couchbase/core/protocol/client_response.hxx +27 -6
  233. data/ext/couchbase/core/protocol/cmd_append.cxx +2 -2
  234. data/ext/couchbase/core/protocol/cmd_cluster_map_change_notification.cxx +2 -2
  235. data/ext/couchbase/core/protocol/cmd_decrement.cxx +6 -6
  236. data/ext/couchbase/core/protocol/cmd_get.cxx +1 -1
  237. data/ext/couchbase/core/protocol/cmd_get_and_lock.cxx +2 -2
  238. data/ext/couchbase/core/protocol/cmd_get_and_touch.cxx +2 -2
  239. data/ext/couchbase/core/protocol/cmd_get_cluster_config.cxx +6 -4
  240. data/ext/couchbase/core/protocol/cmd_get_collection_id.cxx +2 -2
  241. data/ext/couchbase/core/protocol/cmd_get_error_map.cxx +1 -1
  242. data/ext/couchbase/core/protocol/cmd_get_meta.cxx +4 -4
  243. data/ext/couchbase/core/protocol/cmd_get_replica.cxx +1 -1
  244. data/ext/couchbase/core/protocol/cmd_hello.cxx +1 -1
  245. data/ext/couchbase/core/protocol/cmd_increment.cxx +6 -6
  246. data/ext/couchbase/core/protocol/cmd_insert.cxx +4 -4
  247. data/ext/couchbase/core/protocol/cmd_lookup_in.cxx +4 -3
  248. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +4 -3
  249. data/ext/couchbase/core/protocol/cmd_mutate_in.cxx +10 -9
  250. data/ext/couchbase/core/protocol/cmd_mutate_in.hxx +10 -10
  251. data/ext/couchbase/core/protocol/cmd_observe_seqno.cxx +7 -7
  252. data/ext/couchbase/core/protocol/cmd_prepend.cxx +2 -2
  253. data/ext/couchbase/core/protocol/cmd_remove.cxx +2 -2
  254. data/ext/couchbase/core/protocol/cmd_replace.cxx +4 -4
  255. data/ext/couchbase/core/protocol/cmd_touch.cxx +1 -1
  256. data/ext/couchbase/core/protocol/cmd_upsert.cxx +4 -4
  257. data/ext/couchbase/core/protocol/datatype.hxx +118 -1
  258. data/ext/couchbase/core/protocol/frame_info_utils.cxx +1 -1
  259. data/ext/couchbase/core/protocol/magic.hxx +4 -0
  260. data/ext/couchbase/core/protocol/magic_fmt.hxx +3 -0
  261. data/ext/couchbase/core/protocol/server_request.hxx +3 -1
  262. data/ext/couchbase/core/protocol/status.cxx +1 -1
  263. data/ext/couchbase/core/protostellar/analytics_converter.hxx +171 -0
  264. data/ext/couchbase/core/protostellar/bucket_admin_converter.hxx +301 -0
  265. data/ext/couchbase/core/protostellar/collection_admin_converter.hxx +92 -0
  266. data/ext/couchbase/core/protostellar/component.cxx +2445 -0
  267. data/ext/couchbase/core/protostellar/component.hxx +391 -0
  268. data/ext/couchbase/core/{columnar/bootstrap_notification_subscriber.hxx → protostellar/compression_settings.hxx} +11 -10
  269. data/ext/couchbase/core/protostellar/credentials.cxx +233 -0
  270. data/ext/couchbase/core/protostellar/credentials.hxx +88 -0
  271. data/ext/couchbase/core/protostellar/dispatcher.cxx +57 -0
  272. data/ext/couchbase/core/protostellar/dispatcher.hxx +482 -0
  273. data/ext/couchbase/core/protostellar/error_utils.cxx +293 -0
  274. data/ext/couchbase/core/protostellar/error_utils.hxx +97 -0
  275. data/ext/couchbase/core/protostellar/json_payload.hxx +49 -0
  276. data/ext/couchbase/core/protostellar/kv_converter.cxx +70 -0
  277. data/ext/couchbase/core/protostellar/kv_converter.hxx +600 -0
  278. data/ext/couchbase/core/protostellar/query_converter.hxx +265 -0
  279. data/ext/couchbase/core/protostellar/query_index_admin_converter.hxx +291 -0
  280. data/ext/couchbase/core/protostellar/query_proto.hxx +73 -0
  281. data/ext/couchbase/core/protostellar/search_converter.hxx +244 -0
  282. data/ext/couchbase/core/protostellar/search_index_admin_converter.hxx +156 -0
  283. data/ext/couchbase/core/protostellar/view_converter.hxx +158 -0
  284. data/ext/couchbase/core/query_stream.cxx +447 -0
  285. data/ext/couchbase/core/query_stream.hxx +141 -0
  286. data/ext/couchbase/core/query_stream_component.cxx +243 -0
  287. data/ext/couchbase/core/query_stream_component.hxx +68 -0
  288. data/ext/couchbase/core/range_scan_load_balancer.cxx +15 -8
  289. data/ext/couchbase/core/range_scan_orchestrator.cxx +15 -3
  290. data/ext/couchbase/core/response_handler.hxx +41 -4
  291. data/ext/couchbase/core/row_streamer.cxx +332 -46
  292. data/ext/couchbase/core/row_streamer.hxx +31 -1
  293. data/ext/couchbase/core/row_streamer_options.hxx +55 -0
  294. data/ext/couchbase/core/sasl/client.h +3 -0
  295. data/ext/couchbase/core/search_scoring.hxx +66 -0
  296. data/ext/couchbase/core/stream_error_details.hxx +69 -0
  297. data/ext/couchbase/core/timeout_defaults.hxx +0 -1
  298. data/ext/couchbase/core/topology/capabilities.hxx +6 -0
  299. data/ext/couchbase/core/topology/capabilities_fmt.hxx +3 -0
  300. data/ext/couchbase/core/topology/configuration.cxx +21 -6
  301. data/ext/couchbase/core/topology/configuration.hxx +29 -8
  302. data/ext/couchbase/core/topology/configuration_json.hxx +3 -0
  303. data/ext/couchbase/core/topology/error_map_json.hxx +3 -1
  304. data/ext/couchbase/core/tracing/threshold_logging_tracer.cxx +70 -4
  305. data/ext/couchbase/core/tracing/threshold_logging_tracer.hxx +10 -0
  306. data/ext/couchbase/core/transactions/active_transaction_record.cxx +0 -34
  307. data/ext/couchbase/core/transactions/attempt_context_impl.cxx +108 -63
  308. data/ext/couchbase/core/transactions/attempt_context_impl.hxx +19 -2
  309. data/ext/couchbase/core/transactions/error_list.hxx +2 -2
  310. data/ext/couchbase/core/transactions/get_multi_fetch.hxx +160 -0
  311. data/ext/couchbase/core/transactions/get_multi_orchestrator.cxx +326 -125
  312. data/ext/couchbase/core/transactions/get_multi_transaction_id.hxx +62 -0
  313. data/ext/couchbase/core/transactions/internal/atr_entry.hxx +22 -2
  314. data/ext/couchbase/core/transactions/internal/transaction_context.hxx +2 -2
  315. data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +23 -1
  316. data/ext/couchbase/core/transactions/internal/utils.hxx +50 -14
  317. data/ext/couchbase/core/transactions/staged_mutation.cxx +23 -18
  318. data/ext/couchbase/core/transactions/staged_mutation.hxx +15 -4
  319. data/ext/couchbase/core/transactions/transaction_context.cxx +55 -40
  320. data/ext/couchbase/core/transactions/transaction_get_result.hxx +16 -0
  321. data/ext/couchbase/core/transactions/transactions.cxx +3 -55
  322. data/ext/couchbase/core/transactions/transactions_cleanup.cxx +165 -72
  323. data/ext/couchbase/core/transactions/transactions_config.cxx +0 -11
  324. data/ext/couchbase/core/transactions/utils.cxx +10 -0
  325. data/ext/couchbase/core/transactions/waitable_op_list.hxx +2 -2
  326. data/ext/couchbase/core/transactions.hxx +9 -9
  327. data/ext/couchbase/core/utils/byteswap.hxx +166 -9
  328. data/ext/couchbase/core/utils/connection_string.cxx +50 -85
  329. data/ext/couchbase/core/utils/connection_string.hxx +15 -0
  330. data/ext/couchbase/core/{columnar/backoff_calculator.hxx → utils/deadline.hxx} +13 -13
  331. data/ext/couchbase/core/utils/duration_parser.cxx +1 -1
  332. data/ext/couchbase/core/utils/json_streaming_lexer.cxx +48 -5
  333. data/ext/couchbase/core/utils/json_streaming_lexer.hxx +4 -1
  334. data/ext/couchbase/core/utils/movable_function.hxx +301 -52
  335. data/ext/couchbase/core/utils/name_codec.hxx +154 -11
  336. data/ext/couchbase/core/{columnar/retry_info.hxx → utils/process_id.hxx} +18 -13
  337. data/ext/couchbase/core/utils/url_codec.cxx +12 -51
  338. data/ext/couchbase/core/websocket_codec.cxx +3 -29
  339. data/ext/couchbase/couchbase/analytics_options.hxx +21 -10
  340. data/ext/couchbase/couchbase/analytics_row.hxx +105 -0
  341. data/ext/couchbase/couchbase/analytics_stream_result.hxx +266 -0
  342. data/ext/couchbase/couchbase/boolean_query.hxx +3 -3
  343. data/ext/couchbase/couchbase/bucket.hxx +57 -0
  344. data/ext/couchbase/couchbase/cluster.hxx +183 -5
  345. data/ext/couchbase/{core/columnar/security_options.hxx → couchbase/cluster_state.hxx} +18 -14
  346. data/ext/couchbase/couchbase/codec/lenient_json_transcoder.hxx +69 -0
  347. data/ext/couchbase/couchbase/collection.hxx +234 -14
  348. data/ext/couchbase/couchbase/common_durability_options.hxx +18 -3
  349. data/ext/couchbase/couchbase/conjunction_query.hxx +1 -1
  350. data/ext/couchbase/couchbase/create_dataset_analytics_options.hxx +6 -1
  351. data/ext/couchbase/couchbase/date_range_query.hxx +2 -2
  352. data/ext/couchbase/couchbase/decrement_options.hxx +1 -1
  353. data/ext/couchbase/couchbase/disjunction_query.hxx +1 -1
  354. data/ext/couchbase/couchbase/error.hxx +32 -0
  355. data/ext/couchbase/couchbase/error_codes.hxx +32 -0
  356. data/ext/couchbase/couchbase/geo_bounding_box_query.hxx +1 -1
  357. data/ext/couchbase/couchbase/geo_distance_query.hxx +1 -1
  358. data/ext/couchbase/couchbase/geo_polygon_query.hxx +1 -1
  359. data/ext/couchbase/couchbase/get_replica_options.hxx +68 -0
  360. data/ext/couchbase/couchbase/get_replica_result.hxx +5 -3
  361. data/ext/couchbase/couchbase/get_replica_strategy.hxx +161 -0
  362. data/ext/couchbase/couchbase/get_result.hxx +1 -0
  363. data/ext/couchbase/couchbase/increment_options.hxx +1 -1
  364. data/ext/couchbase/couchbase/insert_options.hxx +1 -1
  365. data/ext/couchbase/couchbase/match_phrase_query.hxx +1 -1
  366. data/ext/couchbase/couchbase/match_query.hxx +1 -1
  367. data/ext/couchbase/couchbase/mutate_in_options.hxx +3 -3
  368. data/ext/couchbase/couchbase/network_options.hxx +1 -1
  369. data/ext/couchbase/couchbase/node_id.hxx +42 -14
  370. data/ext/couchbase/couchbase/node_id_for_options.hxx +7 -1
  371. data/ext/couchbase/couchbase/node_ids_options.hxx +8 -2
  372. data/ext/couchbase/couchbase/numeric_range_query.hxx +1 -1
  373. data/ext/couchbase/couchbase/phrase_query.hxx +1 -1
  374. data/ext/couchbase/couchbase/prefix_query.hxx +1 -1
  375. data/ext/couchbase/couchbase/query_options.hxx +23 -12
  376. data/ext/couchbase/couchbase/query_row.hxx +111 -0
  377. data/ext/couchbase/couchbase/query_stream_result.hxx +302 -0
  378. data/ext/couchbase/couchbase/query_string_query.hxx +3 -3
  379. data/ext/couchbase/couchbase/regexp_query.hxx +1 -1
  380. data/ext/couchbase/couchbase/replace_options.hxx +3 -3
  381. data/ext/couchbase/couchbase/result.hxx +14 -3
  382. data/ext/couchbase/couchbase/scan_result_item.hxx +1 -0
  383. data/ext/couchbase/couchbase/scope.hxx +85 -2
  384. data/ext/couchbase/couchbase/search_options.hxx +58 -8
  385. data/ext/couchbase/couchbase/search_scoring.hxx +94 -0
  386. data/ext/couchbase/couchbase/search_scoring_none.hxx +51 -0
  387. data/ext/couchbase/couchbase/search_scoring_reciprocal_rank_fusion.hxx +88 -0
  388. data/ext/couchbase/couchbase/search_scoring_relative_score_fusion.hxx +70 -0
  389. data/ext/couchbase/couchbase/term_range_query.hxx +1 -1
  390. data/ext/couchbase/couchbase/tracing/request_span.hxx +46 -0
  391. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +23 -21
  392. data/ext/couchbase/couchbase/transactions/transaction_get_multi_result.hxx +24 -19
  393. data/ext/couchbase/couchbase/transactions/transaction_get_result.hxx +17 -3
  394. data/ext/couchbase/couchbase/transactions/transactions_config.hxx +4 -2
  395. data/ext/couchbase/couchbase/transactions.hxx +27 -18
  396. data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
  397. data/ext/couchbase/couchbase/vector_query.hxx +1 -1
  398. data/ext/couchbase/couchbase/wait_until_ready_options.hxx +111 -0
  399. data/ext/couchbase/couchbase/wildcard_query.hxx +1 -1
  400. data/ext/extconf.rb +15 -10
  401. data/ext/rcb_analytics.cxx +82 -102
  402. data/ext/rcb_backend.cxx +42 -36
  403. data/ext/rcb_backend.hxx +2 -0
  404. data/ext/rcb_buckets.cxx +47 -59
  405. data/ext/rcb_collections.cxx +42 -52
  406. data/ext/rcb_crud.cxx +345 -224
  407. data/ext/rcb_diagnostics.cxx +117 -27
  408. data/ext/rcb_exceptions.cxx +152 -99
  409. data/ext/rcb_exceptions.hxx +25 -0
  410. data/ext/rcb_extras.cxx +18 -19
  411. data/ext/rcb_hdr_histogram.cxx +19 -4
  412. data/ext/rcb_logger.cxx +202 -27
  413. data/ext/rcb_logger.hxx +12 -0
  414. data/ext/rcb_multi.cxx +40 -40
  415. data/ext/rcb_observability.cxx +7 -5
  416. data/ext/rcb_query.cxx +99 -142
  417. data/ext/rcb_range_scan.cxx +41 -35
  418. data/ext/rcb_search.cxx +98 -99
  419. data/ext/rcb_users.cxx +36 -56
  420. data/ext/rcb_utils.cxx +100 -37
  421. data/ext/rcb_utils.hxx +287 -33
  422. data/ext/rcb_version.cxx +1 -1
  423. data/ext/rcb_views.cxx +55 -51
  424. data/lib/active_support/cache/couchbase_store.rb +77 -12
  425. data/lib/couchbase/bucket.rb +20 -0
  426. data/lib/couchbase/cluster.rb +17 -0
  427. data/lib/couchbase/collection.rb +36 -1
  428. data/lib/couchbase/errors.rb +18 -0
  429. data/lib/couchbase/fork_hooks.rb +16 -7
  430. data/lib/couchbase/get_replica_strategy.rb +89 -0
  431. data/lib/couchbase/logger.rb +1 -1
  432. data/lib/couchbase/management/analytics_index_manager.rb +15 -15
  433. data/lib/couchbase/management/bucket_manager.rb +6 -6
  434. data/lib/couchbase/management/collection_manager.rb +6 -6
  435. data/lib/couchbase/management/query_index_manager.rb +7 -7
  436. data/lib/couchbase/management/search_index_manager.rb +13 -13
  437. data/lib/couchbase/management/user_manager.rb +10 -10
  438. data/lib/couchbase/management/view_index_manager.rb +5 -5
  439. data/lib/couchbase/options.rb +174 -42
  440. data/lib/couchbase/protostellar/bucket.rb +4 -0
  441. data/lib/couchbase/protostellar/cluster.rb +4 -0
  442. data/lib/couchbase/protostellar/collection.rb +4 -0
  443. data/lib/couchbase/protostellar/error_handling.rb +2 -1
  444. data/lib/couchbase/protostellar/request.rb +1 -0
  445. data/lib/couchbase/protostellar/request_generator/kv.rb +16 -2
  446. data/lib/couchbase/protostellar/request_generator/search.rb +17 -1
  447. data/lib/couchbase/protostellar/retry/orchestrator.rb +3 -2
  448. data/lib/couchbase/protostellar/retry/strategies/fail_fast.rb +33 -0
  449. data/lib/couchbase/protostellar/retry/strategies.rb +1 -0
  450. data/lib/couchbase/search_options.rb +95 -6
  451. data/lib/couchbase/version.rb +1 -1
  452. metadata +91 -33
  453. data/ext/0001-fix-build-for-mingw-w64-ucrt-x86_64-toolchain.patch +0 -40
  454. data/ext/couchbase/core/columnar/agent.hxx +0 -71
  455. data/ext/couchbase/core/columnar/agent_config.hxx +0 -59
  456. data/ext/couchbase/core/columnar/background_bootstrap_listener.hxx +0 -42
  457. data/ext/couchbase/core/columnar/backoff_calculator.cxx +0 -62
  458. data/ext/couchbase/core/columnar/columnar_agent.cxx +0 -147
  459. data/ext/couchbase/core/columnar/columnar_agent_config.cxx +0 -48
  460. data/ext/couchbase/core/columnar/columnar_error.cxx +0 -51
  461. data/ext/couchbase/core/columnar/database_management_options.hxx +0 -56
  462. data/ext/couchbase/core/columnar/error.hxx +0 -51
  463. data/ext/couchbase/core/columnar/error_codes.cxx +0 -107
  464. data/ext/couchbase/core/columnar/error_codes.hxx +0 -75
  465. data/ext/couchbase/core/columnar/management_component.cxx +0 -300
  466. data/ext/couchbase/core/columnar/management_component.hxx +0 -56
  467. data/ext/couchbase/core/columnar/query_component.cxx +0 -523
  468. data/ext/couchbase/core/columnar/query_component.hxx +0 -57
  469. data/ext/couchbase/core/columnar/query_options.hxx +0 -59
  470. data/ext/couchbase/core/columnar/query_result.cxx +0 -130
  471. data/ext/couchbase/core/columnar/query_result.hxx +0 -78
  472. data/ext/couchbase/core/io/config_tracker.cxx +0 -687
  473. data/ext/couchbase/core/io/config_tracker.hxx +0 -123
@@ -201,11 +201,14 @@ attempt_context_impl::check_and_handle_blocking_transactions(
201
201
  if (err) {
202
202
  return cb(err);
203
203
  }
204
- return check_atr_entry_for_blocking_document(doc,
205
- exp_delay(std::chrono::milliseconds(50),
206
- std::chrono::milliseconds(500),
207
- std::chrono::seconds(1)),
208
- std::move(cb));
204
+ return check_atr_entry_for_blocking_document(
205
+ doc,
206
+ std::make_shared<async_timed_exp_delay>(
207
+ std::make_shared<asio::steady_timer>(cluster_ref().io_context()),
208
+ std::chrono::milliseconds(50),
209
+ std::chrono::milliseconds(500),
210
+ std::chrono::seconds(1)),
211
+ std::move(cb));
209
212
  }
210
213
  CB_ATTEMPT_CTX_LOG_DEBUG(this,
211
214
  "doc \"{}\" is in another transaction {}, but doesn't have enough "
@@ -919,7 +922,7 @@ attempt_context_impl::replace(const transaction_get_result& document,
919
922
  CB_ATTEMPT_CTX_LOG_TRACE(
920
923
  self, "replacing {} with {}", document, utils::to_string(content.data));
921
924
  self->check_if_done(cb);
922
- staged_mutation* existing_sm = self->staged_mutations_->find_any(document.id());
925
+ const staged_mutation* existing_sm = self->staged_mutations_->find_any(document.id());
923
926
  if (existing_sm != nullptr && existing_sm->type() == staged_mutation_type::REMOVE) {
924
927
  CB_ATTEMPT_CTX_LOG_DEBUG(
925
928
  self, "found existing REMOVE of {} while replacing", document);
@@ -972,9 +975,7 @@ attempt_context_impl::replace(const transaction_get_result& document,
972
975
  self->create_staged_insert(document.id(),
973
976
  std::move(content),
974
977
  document.cas().value(),
975
- exp_delay(std::chrono::milliseconds(5),
976
- std::chrono::milliseconds(300),
977
- self->overall()->config().timeout),
978
+ self->staged_insert_delay(),
978
979
  op_id,
979
980
  std::move(cb));
980
981
  return;
@@ -1312,7 +1313,7 @@ attempt_context_impl::insert(const core::document_id& id,
1312
1313
  try {
1313
1314
  self->check_if_done(cb);
1314
1315
  auto op_id = uid_generator::next();
1315
- staged_mutation* existing_sm = self->staged_mutations_->find_any(id);
1316
+ const staged_mutation* existing_sm = self->staged_mutations_->find_any(id);
1316
1317
  if ((existing_sm != nullptr) &&
1317
1318
  (existing_sm->type() == staged_mutation_type::INSERT ||
1318
1319
  existing_sm->type() == staged_mutation_type::REPLACE)) {
@@ -1345,14 +1346,8 @@ attempt_context_impl::insert(const core::document_id& id,
1345
1346
  std::move(cb));
1346
1347
  }
1347
1348
  const std::uint64_t cas = 0;
1348
- self->create_staged_insert(id,
1349
- std::move(content),
1350
- cas,
1351
- exp_delay(std::chrono::milliseconds(5),
1352
- std::chrono::milliseconds(300),
1353
- self->overall()->config().timeout),
1354
- op_id,
1355
- std::move(cb));
1349
+ self->create_staged_insert(
1350
+ id, std::move(content), cas, self->staged_insert_delay(), op_id, std::move(cb));
1356
1351
  });
1357
1352
  } catch (const std::exception& e) {
1358
1353
  return self->op_completed_with_error(
@@ -1401,19 +1396,24 @@ attempt_context_impl::select_atr_if_needed_unlocked(
1401
1396
  CB_ATTEMPT_CTX_LOG_ERROR(this, "unexpected error \"{}\" during select atr if needed", e.what());
1402
1397
  }
1403
1398
  }
1404
- template<typename Handler, typename Delay>
1399
+ template<typename Handler>
1405
1400
  void
1406
- attempt_context_impl::check_atr_entry_for_blocking_document(const transaction_get_result& doc,
1407
- Delay delay,
1408
- Handler&& cb)
1401
+ attempt_context_impl::check_atr_entry_for_blocking_document(
1402
+ const transaction_get_result& doc,
1403
+ std::shared_ptr<async_timed_exp_delay> delay,
1404
+ Handler&& cb)
1409
1405
  {
1410
- try {
1411
- delay();
1412
- return hooks_.before_check_atr_entry_for_blocking_doc(
1413
- shared_from_this(),
1406
+ (*delay)([self = shared_from_this(), doc, delay, cb = std::forward<Handler>(cb)](
1407
+ const std::exception_ptr& delay_err) mutable {
1408
+ if (delay_err) {
1409
+ return cb(transaction_operation_failed(FAIL_WRITE_WRITE_CONFLICT,
1410
+ "document is in another transaction")
1411
+ .retry());
1412
+ }
1413
+ return self->hooks_.before_check_atr_entry_for_blocking_doc(
1414
+ self,
1414
1415
  doc.id().key(),
1415
- [self = shared_from_this(), delay = std::move(delay), cb = std::forward<Handler>(cb), doc](
1416
- auto ec) mutable {
1416
+ [self, delay = std::move(delay), cb = std::move(cb), doc](auto ec) mutable {
1417
1417
  if (ec) {
1418
1418
  return cb(transaction_operation_failed(FAIL_WRITE_WRITE_CONFLICT,
1419
1419
  "document is in another transaction")
@@ -1453,7 +1453,8 @@ attempt_context_impl::check_atr_entry_for_blocking_document(const transaction_ge
1453
1453
  "existing atr entry found in state {}, retrying",
1454
1454
  attempt_state_name(it->state()));
1455
1455
  }
1456
- return self->check_atr_entry_for_blocking_document(doc, delay, std::move(cb));
1456
+ return self->check_atr_entry_for_blocking_document(
1457
+ doc, std::move(delay), std::move(cb));
1457
1458
  }
1458
1459
  }
1459
1460
  CB_ATTEMPT_CTX_LOG_DEBUG(self, "no blocking atr entry");
@@ -1465,11 +1466,7 @@ attempt_context_impl::check_atr_entry_for_blocking_document(const transaction_ge
1465
1466
  .retry());
1466
1467
  });
1467
1468
  });
1468
- } catch (const retry_operation_timeout&) {
1469
- return cb(
1470
- transaction_operation_failed(FAIL_WRITE_WRITE_CONFLICT, "document is in another transaction")
1471
- .retry());
1472
- }
1469
+ });
1473
1470
  }
1474
1471
  void
1475
1472
  attempt_context_impl::remove(const transaction_get_result& document, VoidCallback&& cb)
@@ -1993,11 +1990,11 @@ dump_request(const core::operations::query_request& req) -> std::string
1993
1990
  std::string raw = "{";
1994
1991
  for (const auto& x : req.raw) {
1995
1992
  raw += x.first;
1996
- raw += ":";
1993
+ raw += ':';
1997
1994
  raw += x.second.str();
1998
- raw += ",";
1995
+ raw += ',';
1999
1996
  }
2000
- raw += "}";
1997
+ raw += '}';
2001
1998
  std::string params;
2002
1999
  for (const auto& x : req.positional_parameters) {
2003
2000
  params.append(x.str());
@@ -2049,6 +2046,7 @@ attempt_context_impl::wrap_query(
2049
2046
 
2050
2047
  if (check_expiry) {
2051
2048
  if (has_expired_client_side(hook_point, std::nullopt)) {
2049
+ query_check_expired_ = true;
2052
2050
  auto err = std::make_exception_ptr(
2053
2051
  transaction_operation_failed(FAIL_EXPIRY,
2054
2052
  fmt::format("{} expired in stage {}", statement, hook_point))
@@ -3241,10 +3239,10 @@ attempt_context_impl::set_atr_pending_locked(
3241
3239
  // This bounds the value to [0-timeout]. It should always be in this
3242
3240
  // range, this is just to protect against the application clock
3243
3241
  // changing.
3244
- const long remaining_bounded_nanos =
3242
+ const auto remaining_bounded_nanos =
3245
3243
  std::max(std::min(remaining.count(), self->overall()->config().timeout.count()),
3246
3244
  static_cast<std::chrono::nanoseconds::rep>(0));
3247
- const long remaining_bounded_msecs = remaining_bounded_nanos / 1'000'000;
3245
+ const auto remaining_bounded_msecs = remaining_bounded_nanos / 1'000'000;
3248
3246
 
3249
3247
  core::operations::mutate_in_request req{ self->atr_id_.value() };
3250
3248
 
@@ -3403,6 +3401,7 @@ attempt_context_impl::do_get(const core::document_id& id,
3403
3401
  return self->get_doc(
3404
3402
  id,
3405
3403
  allow_replica,
3404
+ std::nullopt,
3406
3405
  [self,
3407
3406
  id,
3408
3407
  allow_replica,
@@ -3578,6 +3577,7 @@ execute_lookup(attempt_context_impl* ctx, LookupInRequest& req, Callback&& cb)
3578
3577
  void
3579
3578
  attempt_context_impl::get_doc(const core::document_id& id,
3580
3579
  bool allow_replica,
3580
+ std::optional<std::chrono::milliseconds> timeout,
3581
3581
  std::function<void(std::optional<error_class>,
3582
3582
  std::optional<external_exception>,
3583
3583
  std::optional<std::string>,
@@ -3601,27 +3601,21 @@ attempt_context_impl::get_doc(const core::document_id& id,
3601
3601
 
3602
3602
  try {
3603
3603
  if (allow_replica) {
3604
- cluster_ref().with_bucket_configuration(
3605
- id.bucket(),
3606
- [this, id, specs, cb = std::move(cb)](
3607
- std::error_code ec, const std::shared_ptr<topology::configuration>& config) {
3608
- if (ec) {
3609
- cb(FAIL_OTHER,
3610
- std::nullopt,
3611
- "failed to check whether access_deleted is supported: " + ec.message(),
3612
- std::nullopt);
3613
- return;
3614
- }
3615
- core::operations::lookup_in_any_replica_request req{ id };
3616
- req.read_preference = couchbase::read_preference::selected_server_group_or_all_available;
3617
- req.specs = specs;
3618
- req.access_deleted = config->capabilities.supports_subdoc_access_deleted();
3619
- execute_lookup(this, req, cb);
3620
- });
3604
+ // Per the transactions spec ("Fetching an individual document") and MB-63241, a replica read
3605
+ // must not be combined with the accessDeleted flag; the reference SDKs omit it on the replica
3606
+ // path. We therefore always read replicas without accessDeleted (so tombstones are not
3607
+ // visible there) and need no bucket-config capability probe.
3608
+ core::operations::lookup_in_any_replica_request req{ id };
3609
+ req.read_preference = couchbase::read_preference::selected_server_group_or_all_available;
3610
+ req.specs = specs;
3611
+ req.access_deleted = false;
3612
+ req.timeout = timeout;
3613
+ execute_lookup(this, req, cb);
3621
3614
  } else {
3622
3615
  core::operations::lookup_in_request req{ id };
3623
3616
  req.access_deleted = true;
3624
3617
  req.specs = specs;
3618
+ req.timeout = timeout;
3625
3619
  execute_lookup(this, req, cb);
3626
3620
  }
3627
3621
  } catch (const std::exception& e) {
@@ -3658,8 +3652,8 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
3658
3652
  transaction_operation_failed(ec, "transient error in insert").retry());
3659
3653
  case FAIL_AMBIGUOUS:
3660
3654
  CB_ATTEMPT_CTX_LOG_DEBUG(this, "FAIL_AMBIGUOUS in create_staged_insert, retrying");
3661
- delay();
3662
- return create_staged_insert(id, content, cas, delay, op_id, std::forward<Handler>(cb));
3655
+ return retry_create_staged_insert(
3656
+ id, content, cas, std::forward<Delay>(delay), op_id, std::forward<Handler>(cb));
3663
3657
  case FAIL_OTHER:
3664
3658
  return op_completed_with_error(
3665
3659
  std::forward<Handler>(cb),
@@ -3720,6 +3714,7 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
3720
3714
  return self->get_doc(
3721
3715
  id,
3722
3716
  false,
3717
+ std::nullopt,
3723
3718
  [self, id, content, op_id, cb = std::forward<Handler>(cb), error_handler, delay](
3724
3719
  std::optional<error_class> ec3,
3725
3720
  std::optional<external_exception> /* cause */,
@@ -3747,8 +3742,7 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
3747
3742
  "create staged insert found existing deleted doc, "
3748
3743
  "retrying with cas {}",
3749
3744
  doc->cas().value());
3750
- delay();
3751
- return self->create_staged_insert(
3745
+ return self->retry_create_staged_insert(
3752
3746
  id, content, doc->cas().value(), delay, op_id, std::forward<Handler>(cb));
3753
3747
  }
3754
3748
  if (!doc->links().is_document_in_transaction()) {
@@ -3800,9 +3794,8 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
3800
3794
  "doc ok to overwrite, retrying create_staged_insert "
3801
3795
  "with cas {}",
3802
3796
  doc->cas().value());
3803
- delay();
3804
- return self->create_staged_insert(
3805
- id, content, doc->cas().value(), delay, op_id, std::forward<Handler>(cb));
3797
+ return self->retry_create_staged_insert(
3798
+ id, content, doc->cas().value(), delay, op_id, std::move(cb));
3806
3799
  });
3807
3800
  } else {
3808
3801
  // no doc now, just retry entire txn
@@ -3828,6 +3821,58 @@ attempt_context_impl::create_staged_insert_error_handler(const core::document_id
3828
3821
  }
3829
3822
  }
3830
3823
 
3824
+ auto
3825
+ attempt_context_impl::staged_insert_delay() const -> std::shared_ptr<async_timed_exp_delay>
3826
+ {
3827
+ auto delay = std::make_shared<async_timed_exp_delay>(
3828
+ std::make_shared<asio::steady_timer>(cluster_ref().io_context()),
3829
+ std::chrono::milliseconds(5),
3830
+ std::chrono::milliseconds(300),
3831
+ overall()->config().timeout);
3832
+ // The transaction expiry bounds the retries; a retry limit would fail a long transaction early.
3833
+ delay->max_retries = std::numeric_limits<std::size_t>::max();
3834
+ return delay;
3835
+ }
3836
+
3837
+ template<typename Handler, typename Delay>
3838
+ void
3839
+ attempt_context_impl::retry_create_staged_insert(const core::document_id& id,
3840
+ const codec::encoded_value& content,
3841
+ std::uint64_t cas,
3842
+ Delay&& delay,
3843
+ const std::string& op_id,
3844
+ Handler&& cb)
3845
+ {
3846
+ auto shared_delay = delay;
3847
+ (*shared_delay)([self = shared_from_this(),
3848
+ id,
3849
+ content = content,
3850
+ cas,
3851
+ delay = std::forward<Delay>(delay),
3852
+ op_id,
3853
+ cb = std::forward<Handler>(cb)](const std::exception_ptr& delay_err) mutable {
3854
+ if (delay_err) {
3855
+ try {
3856
+ std::rethrow_exception(delay_err);
3857
+ } catch (const retry_operation_timeout&) {
3858
+ return self->create_staged_insert_error_handler(id,
3859
+ content,
3860
+ cas,
3861
+ delay,
3862
+ op_id,
3863
+ std::move(cb),
3864
+ FAIL_EXPIRY,
3865
+ UNKNOWN,
3866
+ "create_staged_insert retries timed out");
3867
+ } catch (const std::exception& e) {
3868
+ return self->op_completed_with_error(std::move(cb),
3869
+ transaction_operation_failed(FAIL_OTHER, e.what()));
3870
+ }
3871
+ }
3872
+ return self->create_staged_insert(id, std::move(content), cas, delay, op_id, std::move(cb));
3873
+ });
3874
+ }
3875
+
3831
3876
  template<typename Handler, typename Delay>
3832
3877
  void
3833
3878
  attempt_context_impl::create_staged_insert(const core::document_id& id,
@@ -74,6 +74,7 @@ class transactions;
74
74
  enum class forward_compat_stage : std::uint8_t;
75
75
  class staged_mutation_queue;
76
76
  class staged_mutation;
77
+ struct async_timed_exp_delay;
77
78
  struct attempt_context_testing_hooks;
78
79
 
79
80
  class attempt_context_impl
@@ -100,6 +101,8 @@ private:
100
101
  friend class atr_cleanup_entry;
101
102
  // transaction_context needs access to the two functions below
102
103
  friend class transaction_context;
104
+ // get_multi fetches raw documents (body + metadata, no MAV/ATR resolution) via get_doc
105
+ friend class get_multi_operation;
103
106
 
104
107
  void insert(const core::document_id& id,
105
108
  codec::encoded_value content,
@@ -461,6 +464,9 @@ public:
461
464
 
462
465
  private:
463
466
  std::atomic<bool> expiry_overtime_mode_{ false };
467
+ // Set when the client-side expiry check of a query statement fires. Any later failed
468
+ // auto-rollback in this attempt raises EXPIRED instead of FAILED.
469
+ std::atomic<bool> query_check_expired_{ false };
464
470
 
465
471
  auto check_expiry_pre_commit(std::string stage, std::optional<const std::string> doc_id) -> bool;
466
472
 
@@ -483,9 +489,9 @@ private:
483
489
  forward_compat_stage stage,
484
490
  std::function<void(std::optional<transaction_operation_failed>)>&& cb);
485
491
 
486
- template<typename Handler, typename Delay>
492
+ template<typename Handler>
487
493
  void check_atr_entry_for_blocking_document(const transaction_get_result& doc,
488
- Delay delay,
494
+ std::shared_ptr<async_timed_exp_delay> delay,
489
495
  Handler&& cb);
490
496
 
491
497
  template<typename Handler>
@@ -513,6 +519,7 @@ private:
513
519
 
514
520
  void get_doc(const core::document_id& id,
515
521
  bool allow_replica,
522
+ std::optional<std::chrono::milliseconds> timeout,
516
523
  std::function<void(std::optional<error_class>,
517
524
  std::optional<external_exception>,
518
525
  std::optional<std::string>,
@@ -540,6 +547,16 @@ private:
540
547
  const std::optional<document_metadata>& document_metadata,
541
548
  Handler&& cb);
542
549
 
550
+ [[nodiscard]] auto staged_insert_delay() const -> std::shared_ptr<async_timed_exp_delay>;
551
+
552
+ template<typename Handler, typename Delay>
553
+ void retry_create_staged_insert(const core::document_id& id,
554
+ const codec::encoded_value& content,
555
+ std::uint64_t cas,
556
+ Delay&& delay,
557
+ const std::string& op_id,
558
+ Handler&& cb);
559
+
543
560
  template<typename Handler, typename Delay>
544
561
  void create_staged_insert_error_handler(const core::document_id& id,
545
562
  const codec::encoded_value& content,
@@ -39,7 +39,7 @@ public:
39
39
  }
40
40
  void push_back(const transaction_operation_failed& ex)
41
41
  {
42
- std::lock_guard<std::mutex> lock(mutex_);
42
+ std::scoped_lock<std::mutex> lock(mutex_);
43
43
  list_.push_back(ex);
44
44
  size_ = list_.size();
45
45
  }
@@ -47,7 +47,7 @@ public:
47
47
  {
48
48
  assert(size_.load() > 0);
49
49
  // merge the errors, throw a composite
50
- std::lock_guard<std::mutex> lock(mutex_);
50
+ std::scoped_lock<std::mutex> lock(mutex_);
51
51
  transaction_operation_failed::merge_errors(list_, cause);
52
52
  }
53
53
  };
@@ -0,0 +1,160 @@
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
+
17
+ #pragma once
18
+
19
+ #include "core/document_id.hxx"
20
+ #include "error_class.hxx"
21
+ #include "exceptions.hxx"
22
+ #include "get_multi_transaction_id.hxx"
23
+ #include "internal/doc_record.hxx"
24
+
25
+ #include <algorithm>
26
+ #include <chrono>
27
+ #include <cstdint>
28
+ #include <optional>
29
+ #include <string>
30
+ #include <vector>
31
+
32
+ // Pure decision helpers for the get_multi orchestrator, factored out of get_multi_orchestrator.cxx
33
+ // so the branching that the ExtGetMulti spec specifies can be unit-tested without a cluster. Each
34
+ // function operates on plain data and is free of I/O or orchestrator state.
35
+ namespace couchbase::core::transactions
36
+ {
37
+ enum class get_multi_fetch_outcome : std::uint8_t {
38
+ document_absent,
39
+ retry_after_backoff,
40
+ bound_exceeded,
41
+ fail_expired,
42
+ fail_without_rollback,
43
+ fail_with_rollback,
44
+ };
45
+
46
+ enum class bound_exceeded_action : std::uint8_t {
47
+ preserve_prior_value,
48
+ fail_retryable,
49
+ };
50
+
51
+ /**
52
+ * Decides what a bound-exceeded fetch does with its slot. A document already fetched in an earlier
53
+ * round is kept for the best-effort snapshot the spec's BoundExceeded return allows. But when no
54
+ * value is held for the document -- e.g. reset_and_retry() cleared every slot and the re-fetch hit
55
+ * a transient with the (fixed, already-elapsed) bound -- leaving the slot blank would misreport the
56
+ * document as not-found: a transient is never a document-not-found. The spec's "Global signal
57
+ * handling" instead raises a retryable error when a document is unresolved at the bound, so the
58
+ * enclosing transaction retries rather than returning a wrong absent result.
59
+ */
60
+ [[nodiscard]] inline auto
61
+ get_multi_bound_exceeded_action(bool has_prior_value) -> bound_exceeded_action
62
+ {
63
+ return has_prior_value ? bound_exceeded_action::preserve_prior_value
64
+ : bound_exceeded_action::fail_retryable;
65
+ }
66
+
67
+ /**
68
+ * Maps a failed individual get_multi fetch to the action required by the ExtGetMulti spec's
69
+ * "Fetching an individual document / Error handling" section, adapted to this SDK's error model.
70
+ *
71
+ * The spec branches on a distinct TimeoutException, which this SDK does not have: a read timeout
72
+ * classifies as FAIL_TRANSIENT (unambiguous_timeout) or FAIL_AMBIGUOUS (ambiguous_timeout), the
73
+ * same classes as a genuine transient failure. Rather than guess which occurred, a transient class
74
+ * is disambiguated by whether the operation's read-skew bound has elapsed: before the bound it is a
75
+ * transient error to retry (spec FAIL_TRANSIENT: exponential backoff from 1ms), once the bound has
76
+ * elapsed the SDK stops resolving and returns the best-effort snapshot (spec BoundExceeded).
77
+ * A FAIL_EXPIRY is surfaced as fail_expired so the caller can mark the operation expired (raising
78
+ * TRANSACTION_EXPIRED) rather than a generic failure.
79
+ */
80
+ [[nodiscard]] inline auto
81
+ classify_get_multi_fetch_error(std::optional<error_class> ec,
82
+ std::optional<external_exception> cause,
83
+ bool read_skew_bound_exceeded) -> get_multi_fetch_outcome
84
+ {
85
+ if (!ec.has_value()) {
86
+ return get_multi_fetch_outcome::document_absent;
87
+ }
88
+ if (cause == DOCUMENT_UNRETRIEVABLE_EXCEPTION || cause == DOCUMENT_NOT_FOUND_EXCEPTION ||
89
+ ec == FAIL_DOC_NOT_FOUND) {
90
+ return get_multi_fetch_outcome::document_absent;
91
+ }
92
+ if (ec == FAIL_TRANSIENT || ec == FAIL_AMBIGUOUS) {
93
+ return read_skew_bound_exceeded ? get_multi_fetch_outcome::bound_exceeded
94
+ : get_multi_fetch_outcome::retry_after_backoff;
95
+ }
96
+ if (ec == FAIL_EXPIRY) {
97
+ return get_multi_fetch_outcome::fail_expired;
98
+ }
99
+ if (ec == FAIL_HARD) {
100
+ return get_multi_fetch_outcome::fail_without_rollback;
101
+ }
102
+ return get_multi_fetch_outcome::fail_with_rollback;
103
+ }
104
+
105
+ /**
106
+ * Per-document read timeout: the smaller of the time left before the operation's read-skew bound
107
+ * and the SDK's key-value read timeout (spec "Fetching an individual document": use an operation
108
+ * timeout of min(remaining deadline, 2.5s)). Floored at 1ms so an already-elapsed bound still
109
+ * issues a bounded read that fails fast rather than passing a zero or negative timeout.
110
+ */
111
+ [[nodiscard]] inline auto
112
+ get_multi_fetch_timeout(std::chrono::steady_clock::duration remaining,
113
+ std::chrono::milliseconds key_value_timeout) -> std::chrono::milliseconds
114
+ {
115
+ const auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(remaining);
116
+ if (remaining_ms < std::chrono::milliseconds{ 1 }) {
117
+ return std::chrono::milliseconds{ 1 };
118
+ }
119
+ return std::min(remaining_ms, key_value_timeout);
120
+ }
121
+
122
+ /**
123
+ * True when a fetched document is a read-skew victim (spec "Read skew resolution", T1 COMMITTED): a
124
+ * document we fetched that is NOT part of T1 -- it carries no transactional metadata, or belongs to
125
+ * a different attempt -- yet which T1's ATR entry records as mutated (inserted/replaced/removed).
126
+ * Such a document was read before T1 committed and must be re-fetched for a consistent snapshot.
127
+ */
128
+ [[nodiscard]] inline auto
129
+ contains_mutation(const std::optional<std::vector<doc_record>>& mutated_ids,
130
+ const core::document_id& id) -> bool
131
+ {
132
+ if (!mutated_ids) {
133
+ return false;
134
+ }
135
+ return std::any_of(mutated_ids->begin(), mutated_ids->end(), [&id](const auto& mutation) {
136
+ return mutation == id;
137
+ });
138
+ }
139
+
140
+ [[nodiscard]] inline auto
141
+ is_read_skew_victim(bool doc_exists,
142
+ const std::optional<transaction_id>& doc_transaction_id,
143
+ const std::string& t1_attempt_id,
144
+ const core::document_id& id,
145
+ const std::optional<std::vector<doc_record>>& t1_inserted_ids,
146
+ const std::optional<std::vector<doc_record>>& t1_replaced_ids,
147
+ const std::optional<std::vector<doc_record>>& t1_removed_ids) -> bool
148
+ {
149
+ if (!doc_exists) {
150
+ return false;
151
+ }
152
+ const bool fetched_as_part_of_t1 =
153
+ doc_transaction_id.has_value() && doc_transaction_id->attempt == t1_attempt_id;
154
+ if (fetched_as_part_of_t1) {
155
+ return false;
156
+ }
157
+ return contains_mutation(t1_inserted_ids, id) || contains_mutation(t1_replaced_ids, id) ||
158
+ contains_mutation(t1_removed_ids, id);
159
+ }
160
+ } // namespace couchbase::core::transactions