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.
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 +136 -23
  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 +82 -7
  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 +1551 -397
  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 +60 -6
  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 +13 -37
  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 +33 -1
  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
@@ -166,34 +166,6 @@ wrap_run(transactions& txns,
166
166
  return overall->get_transaction_result();
167
167
  }
168
168
 
169
- template<typename Handler>
170
- auto
171
- wrap_public_api_run(transactions& txns,
172
- const couchbase::transactions::transaction_options& config,
173
- std::size_t max_attempts,
174
- Handler&& fn) -> ::couchbase::transactions::transaction_result
175
- {
176
- return wrap_run(txns, config, max_attempts, [fn = std::forward<Handler>(fn)](const auto& ctx) {
177
- const couchbase::error err = fn(ctx);
178
- if (err && err.ec() != errc::transaction_op::transaction_op_failed) {
179
- // We intentionally don't handle transaction_op_failed here, as we must have cached the
180
- // transaction error internally already, which has the full context with the right error class
181
- // etc.
182
- if (err.ec().category() == core::impl::transaction_op_category()) {
183
- throw op_exception(err);
184
- }
185
- throw std::system_error(err.ec(), fmt::format("{}", err));
186
- }
187
- });
188
- }
189
-
190
- auto
191
- transactions::run(logic&& code) -> ::couchbase::transactions::transaction_result
192
- {
193
- const couchbase::transactions::transaction_options config;
194
- return wrap_run(*this, config, max_attempts_, std::move(code));
195
- }
196
-
197
169
  auto
198
170
  transactions::run(const couchbase::transactions::transaction_options& config, logic&& code)
199
171
  -> ::couchbase::transactions::transaction_result
@@ -202,17 +174,9 @@ transactions::run(const couchbase::transactions::transaction_options& config, lo
202
174
  }
203
175
 
204
176
  auto
205
- transactions::run(couchbase::transactions::txn_logic&& code,
206
- const couchbase::transactions::transaction_options& config)
207
- -> std::pair<couchbase::error, couchbase::transactions::transaction_result>
177
+ transactions::run(logic&& code) -> ::couchbase::transactions::transaction_result
208
178
  {
209
- try {
210
- return { {}, wrap_public_api_run(*this, config, max_attempts_, std::move(code)) };
211
- } catch (const transaction_exception& e) {
212
- // get error from e and return it in the transaction_result
213
- auto [err_ctx, result] = e.get_transaction_result();
214
- return std::make_pair(core::impl::make_error(err_ctx), result);
215
- }
179
+ return run({}, std::move(code));
216
180
  }
217
181
 
218
182
  void
@@ -229,27 +193,11 @@ transactions::run(const couchbase::transactions::transaction_options& config,
229
193
  }
230
194
  }).detach();
231
195
  }
232
- void
233
- transactions::run(couchbase::transactions::async_txn_logic&& code,
234
- couchbase::transactions::async_txn_complete_logic&& cb,
235
- const couchbase::transactions::transaction_options& config)
236
- {
237
- std::thread([this, config, code = std::move(code), cb = std::move(cb)]() {
238
- try {
239
- auto result = wrap_public_api_run(*this, config, max_attempts_, code);
240
- return cb({}, result);
241
- } catch (const transaction_exception& e) {
242
- auto [ctx, res] = e.get_transaction_result();
243
- return cb(core::impl::make_error(ctx), res);
244
- }
245
- }).detach();
246
- }
247
196
 
248
197
  void
249
198
  transactions::run(async_logic&& code, txn_complete_callback&& cb)
250
199
  {
251
- const couchbase::transactions::transaction_options config;
252
- return run(config, std::move(code), std::move(cb));
200
+ return run({}, std::move(code), std::move(cb));
253
201
  }
254
202
 
255
203
  void
@@ -27,10 +27,13 @@
27
27
  #include "internal/utils.hxx"
28
28
 
29
29
  #include "core/operations.hxx"
30
- #include "core/utils/byteswap.hxx"
30
+ #include "core/utils/process_id.hxx"
31
31
 
32
32
  #include <couchbase/fmt/transaction_keyspace.hxx>
33
33
 
34
+ #include <asio/post.hpp>
35
+
36
+ #include <deque>
34
37
  #include <functional>
35
38
  #include <future>
36
39
  #include <list>
@@ -55,34 +58,13 @@ transactions_cleanup::transactions_cleanup(
55
58
  : cluster_(std::move(cluster))
56
59
  , config_(std::move(config))
57
60
  , client_uuid_(uid_generator::next())
61
+ , client_record_owner_pid_(utils::current_process_id())
58
62
  {
59
63
  start();
60
64
  }
61
65
 
62
66
  namespace
63
67
  {
64
- /**
65
- * ${Mutation.CAS} is written by kvengine with
66
- * 'macroToString(htonll(info.cas))'. Discussed this with KV team and, though
67
- * there is consensus that this is off (htonll is definitely wrong, and a string
68
- * is an odd choice), there are clients (SyncGateway) that consume the current
69
- * string, so it can't be changed. Note that only little-endian servers are
70
- * supported for Couchbase, so the 8 byte long inside the string will always be
71
- * little-endian ordered.
72
- *
73
- * Looks like: "0x000058a71dd25c15"
74
- * Want: 0x155CD21DA7580000 (1539336197457313792 in base10, an epoch
75
- * time in millionths of a second)
76
- */
77
- auto
78
- parse_mutation_cas(const std::string& cas) -> std::uint64_t
79
- {
80
- if (cas.empty()) {
81
- return 0;
82
- }
83
- return core::utils::byte_swap(std::stoull(cas, nullptr, 16)) / 1000000;
84
- }
85
-
86
68
  // TODO(CXXCBC-549)
87
69
  const std::string CLIENT_RECORD_DOC_ID = "_txn:client-record"; // NOLINT(cert-err58-cpp)
88
70
  } // namespace
@@ -111,7 +93,7 @@ transactions_cleanup::clean_collection(
111
93
  // first make sure the collection is in the list
112
94
  while (is_running()) {
113
95
  {
114
- const std::lock_guard<std::mutex> lock(mutex_);
96
+ const std::scoped_lock<std::mutex> lock(mutex_);
115
97
  if (collections_.end() == std::find(collections_.begin(), collections_.end(), keyspace)) {
116
98
  CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG(
117
99
  "cleanup for {} ending, no longer in collection cleanup list", keyspace);
@@ -134,54 +116,125 @@ transactions_cleanup::clean_collection(
134
116
  all_atrs.size(),
135
117
  config_.cleanup_config.cleanup_window.count());
136
118
 
119
+ // Each ATR is checked with a blocking get_atr. If those were run strictly one-at-a-time the
120
+ // scan rate would be capped by the per-ATR KV latency rather than the cleanup window: on a
121
+ // slow or loaded KV node a single window could fail to scan all ATRs, leaving lost
122
+ // transactions (and the documents they block) uncleaned for far longer than configured. So
123
+ // run the checks with a bounded number in flight: launches are still paced across the window
124
+ // (to avoid flooding the cluster), but a slow lookup no longer holds up the ones behind it.
125
+ std::deque<std::future<void>> in_flight;
126
+ // The pool is created in start() whenever lost-attempts cleanup is enabled, which is the only
127
+ // configuration that spawns collection workers, so it is always present here. Bind a checked
128
+ // reference now rather than dereferencing the optional at each launch.
129
+ if (!atr_cleanup_pool_.has_value()) {
130
+ // Should be unreachable (see above). If the invariant is ever broken, surface it: a silent
131
+ // return here disables lost-attempts cleanup for this collection with no other signal.
132
+ CB_LOST_ATTEMPT_CLEANUP_LOG_ERROR(
133
+ "atr cleanup pool is not initialized; skipping lost-attempts cleanup for {}", keyspace);
134
+ return;
135
+ }
136
+ auto& atr_cleanup_pool = *atr_cleanup_pool_;
137
+ const auto reclaim_one = [&in_flight]() {
138
+ const auto reclaim = [](std::future<void>& f) {
139
+ // get() only rethrows if the task itself threw, which it is written not to, but guard
140
+ // anyway so one bad ATR cannot abort the whole scan.
141
+ try {
142
+ f.get();
143
+ // NOLINTNEXTLINE(bugprone-empty-catch) -- task already logged any error
144
+ } catch (...) {
145
+ // already handled in the task
146
+ }
147
+ };
148
+ // Reclaim any already-finished check first, so a slow lookup at the front does not hold up
149
+ // the ready ones queued behind it.
150
+ for (auto it = in_flight.begin(); it != in_flight.end(); ++it) {
151
+ if (it->wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
152
+ reclaim(*it);
153
+ in_flight.erase(it);
154
+ return;
155
+ }
156
+ }
157
+ // None ready yet: block on the oldest, since a slot must be freed before launching more.
158
+ reclaim(in_flight.front());
159
+ in_flight.pop_front();
160
+ };
161
+
137
162
  for (auto it = all_atrs.begin() + details.index_of_this_client; it < all_atrs.end();
138
163
  it += details.num_active_clients) {
139
- auto atrs_left_for_this_client =
140
- std::distance(it, all_atrs.end()) /
141
- std::max<decltype(it)::difference_type>(1, details.num_active_clients);
164
+ if (!is_running()) {
165
+ break;
166
+ }
167
+ // This client visits every num_active_clients-th ATR from `it`, so the number it still has
168
+ // to launch is ceil(remaining / stride). Floor division would undercount at the tail,
169
+ // inflating budget_for_this_atr below and over-pacing the scan past the cleanup window.
170
+ const auto stride = std::max<decltype(it)::difference_type>(1, details.num_active_clients);
171
+ const auto atrs_left_for_this_client =
172
+ (std::distance(it, all_atrs.end()) + stride - 1) / stride;
142
173
  const auto now = std::chrono::steady_clock::now();
143
174
  const auto elapsed_in_cleanup_window =
144
175
  std::chrono::duration_cast<std::chrono::microseconds>(now - start);
145
176
  const auto remaining_in_cleanup_window = cleanup_window - elapsed_in_cleanup_window;
177
+ // Divide the time left in the window evenly among the ATRs still to launch, so the scan is
178
+ // spread across the window rather than issued as a burst.
146
179
  const auto budget_for_this_atr = std::chrono::microseconds(
147
180
  remaining_in_cleanup_window.count() /
148
181
  std::max<decltype(it)::difference_type>(1, atrs_left_for_this_client));
149
- // clean the ATR entry
150
- std::string atr_id = *it;
151
- if (!is_running()) {
152
- CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG("cleanup of {} complete", keyspace);
153
- return;
154
- }
155
-
156
- try {
157
- handle_atr_cleanup({ keyspace.bucket, keyspace.scope, keyspace.collection, atr_id });
158
- } catch (const std::exception& e) {
159
- CB_LOST_ATTEMPT_CLEANUP_LOG_ERROR(
160
- "cleanup of atr {} failed with {}, moving on", atr_id, e.what());
182
+ // Bound concurrency: reclaim a finished slot before launching the next check.
183
+ while (in_flight.size() >= max_in_flight_atr_checks_) {
184
+ reclaim_one();
161
185
  }
162
186
 
163
- const auto atr_end = std::chrono::steady_clock::now();
164
- const auto atr_used = std::chrono::duration_cast<std::chrono::microseconds>(atr_end - now);
165
- const auto atr_left = budget_for_this_atr - atr_used;
166
-
167
- // Too verbose to log, but leaving here commented as it may be useful later for internal
168
- // debugging
169
- /*CB_LOST_ATTEMPT_CLEANUP_LOG_INFO("{} {} atrs_left_for_this_client={}
170
- elapsed_in_cleanup_window={}us " "remaining_in_cleanup_window={}us
171
- budget_for_this_atr={}us atr_used={}us atr_left={}us", bucket_name, atr_id,
172
- atrs_left_for_this_client, elapsed_in_cleanup_window.count(),
173
- remaining_in_cleanup_window.count(),
174
- budget_for_this_atr.count(),
175
- atr_used.count(),
176
- atr_left.count());*/
177
-
178
- if (atr_left.count() > 0 &&
179
- atr_left.count() < 1000000000) { // safety check protects against bugs
180
- if (!interruptable_wait(atr_left)) {
181
- return;
187
+ const std::string atr_id = *it;
188
+ // Offload the blocking check to the shared bounded pool (never the IO thread); keep its
189
+ // future so the loop can bound how many run concurrently and drain them at the end.
190
+ // packaged_task is wrapped in a shared_ptr because asio::post needs a copyable handler.
191
+ auto task = std::make_shared<std::packaged_task<void()>>([this, keyspace, atr_id]() {
192
+ try {
193
+ handle_atr_cleanup({ keyspace.bucket, keyspace.scope, keyspace.collection, atr_id });
194
+ } catch (const std::exception& e) {
195
+ CB_LOST_ATTEMPT_CLEANUP_LOG_ERROR(
196
+ "cleanup of atr {} failed with {}, moving on", atr_id, e.what());
197
+ }
198
+ });
199
+ auto future = task->get_future();
200
+ // Post before recording the future. If asio::post() throws (e.g. allocation failure) the
201
+ // task never runs; recording its future first would leave a future in `in_flight` that
202
+ // never becomes ready, deadlocking reclaim_one()/the drain. On throw, `future` is discarded
203
+ // and the exception unwinds to the per-collection retry below.
204
+ asio::post(atr_cleanup_pool, [task]() {
205
+ (*task)();
206
+ });
207
+ in_flight.emplace_back(std::move(future));
208
+
209
+ // Pace the launch cadence so the scan spreads across the window rather than bursting.
210
+ // Subtract the time already spent this iteration -- reclaiming a slot can block on a slow
211
+ // lookup, and posting takes time -- from the budget, so a slow iteration does not push the
212
+ // scan past the cleanup window. If we are already behind, the remainder is <= 0 and we
213
+ // proceed immediately.
214
+ const auto work_time = std::chrono::duration_cast<std::chrono::microseconds>(
215
+ std::chrono::steady_clock::now() - now);
216
+ const auto sleep_for_this_atr = budget_for_this_atr - work_time;
217
+ if (sleep_for_this_atr.count() > 0 &&
218
+ sleep_for_this_atr.count() < 1000000000) { // safety check protects against bugs
219
+ if (!interruptable_wait(sleep_for_this_atr)) {
220
+ break;
182
221
  }
183
222
  }
184
223
  }
224
+
225
+ // Wait for any still-running checks before completing the run. On stop() the launch loop
226
+ // above has already stopped starting new checks (it breaks on !is_running()), so this only
227
+ // drains the
228
+ // <= max_in_flight_atr_checks_ already in flight. They are reclaimed rather than abandoned
229
+ // because each task references this cleanup state; teardown therefore blocks on them, bounded
230
+ // by the KV read timeout (an in-flight get_atr is not cancelled).
231
+ while (!in_flight.empty()) {
232
+ reclaim_one();
233
+ }
234
+ if (!is_running()) {
235
+ CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG("cleanup of {} complete", keyspace);
236
+ return;
237
+ }
185
238
  } catch (const std::exception& ex) {
186
239
  CB_LOST_ATTEMPT_CLEANUP_LOG_ERROR("cleanup failed with {}, trying again in 3 sec...",
187
240
  ex.what());
@@ -590,10 +643,10 @@ transactions_cleanup::add_collection(const couchbase::transactions::transaction_
590
643
  if (it == collections_.end()) {
591
644
  collections_.emplace_back(keyspace);
592
645
  // Spawn only while running. stop() takes the worker handles out and joins them, so a
593
- // handle appended after that point is one nothing has agreed to join -- and a joinable
594
- // std::thread reaching its destructor terminates. Registering the keyspace is still
595
- // right: start() spawns a worker for everything in collections_, so cleanup resumes for
596
- // it on the next start.
646
+ // handle appended after that point is one nothing has agreed to join -- and a
647
+ // joinable std::thread that reaches its destructor terminates. Registering the
648
+ // keyspace is still right: start() spawns a worker for everything in collections_,
649
+ // so cleanup resumes for it on the next start.
597
650
  if (running_) {
598
651
  // start cleaning right away
599
652
  lost_attempt_cleanup_workers_.emplace_back([this, keyspace = collections_.back()]() {
@@ -609,17 +662,34 @@ transactions_cleanup::add_collection(const couchbase::transactions::transaction_
609
662
  void
610
663
  transactions_cleanup::start()
611
664
  {
665
+ // The pool has to exist before any worker does: clean_collection() treats a missing pool
666
+ // as a broken invariant and gives up on the keyspace.
667
+ if (config_.cleanup_config.cleanup_lost_attempts) {
668
+ // Blocking per-ATR checks are offloaded to this bounded pool, never the single IO thread.
669
+ atr_cleanup_pool_.emplace(max_in_flight_atr_checks_);
670
+ }
671
+
612
672
  {
673
+ // running_ is read under mutex_ everywhere else -- is_running(), interruptable_wait(),
674
+ // add_collection() and stop() -- so it is written under it here too, rather than being
675
+ // the one unsynchronized access to it.
676
+ //
677
+ // Publishing it in the same critical section as the respawn below also settles who
678
+ // spawns what. A concurrent add_collection() either runs before this section and only
679
+ // registers its keyspace, which the loop then spawns for, or after it and spawns for
680
+ // itself, by which point the loop has already gone past. Exactly one worker per
681
+ // keyspace either way.
613
682
  const std::unique_lock<std::mutex> lock(mutex_);
614
683
  running_ = config_.cleanup_config.cleanup_client_attempts ||
615
684
  config_.cleanup_config.cleanup_lost_attempts;
616
685
 
617
686
  if (config_.cleanup_config.cleanup_lost_attempts) {
618
- // Re-spawn a worker for every keyspace still registered from before a previous stop().
619
- // add_collection() below cannot do this: it dedupes on collections_, so an
620
- // already-registered keyspace is skipped and its lost-attempt cleanup stays dead for the
621
- // rest of the process. notify_fork(prepare) calls stop() and notify_fork(parent) calls
622
- // start() again, so every fork leaves exactly that state behind unless it's undone here.
687
+ // Re-spawn a worker for every keyspace still registered from before a previous
688
+ // stop(). add_collection() below cannot do it: it dedupes on collections_, so
689
+ // for an already-registered keyspace it skips the worker and lost-attempt
690
+ // cleanup stays dead for the rest of the process. Any fork leaves exactly that
691
+ // state behind -- notify_fork(prepare) stops cleanup, notify_fork(parent)
692
+ // starts it again.
623
693
  for (const auto& keyspace : collections_) {
624
694
  lost_attempt_cleanup_workers_.emplace_back([this, keyspace]() {
625
695
  this->clean_collection(keyspace);
@@ -654,10 +724,20 @@ transactions_cleanup::stop()
654
724
  cleanup_thr_.join();
655
725
  CB_ATTEMPT_CLEANUP_LOG_DEBUG("cleanup attempt thread closed");
656
726
  }
657
- // Take the handles out under mutex_, then join with the lock released: add_collection() and
658
- // start() append to this list while holding mutex_, so walking it live would race with them;
659
- // and the workers themselves take mutex_ (is_running(), interruptable_wait()), so joining
660
- // while still holding it would deadlock.
727
+ // Take the handles out under mutex_, then join them with the lock released.
728
+ //
729
+ // Under the lock, because add_collection() and start() append to this list while
730
+ // holding mutex_. Walking it while another thread appends is a data race on the
731
+ // list itself, and an append that lands after it was emptied would leave a
732
+ // joinable std::thread to be destroyed later, which terminates.
733
+ //
734
+ // With the lock released, because the workers themselves take mutex_ to see
735
+ // running_ (interruptable_wait, and the collections_ lookup in clean_collection),
736
+ // so joining while holding it deadlocks.
737
+ //
738
+ // Moving the handles out also drops them, which a later start() needs in order to
739
+ // spawn fresh workers. collections_ is deliberately kept: it also holds keyspaces
740
+ // registered at runtime, and discarding those would silently stop cleaning them.
661
741
  std::list<std::thread> workers;
662
742
  {
663
743
  const std::unique_lock<std::mutex> lock(mutex_);
@@ -669,6 +749,16 @@ transactions_cleanup::stop()
669
749
  t.join();
670
750
  }
671
751
  }
752
+ // Workers drain their in-flight checks before returning, so the pool is now idle. stop() before
753
+ // join(): the pool's threads wait for work, so join() alone would block indefinitely on an idle
754
+ // pool; stop() requests shutdown so join() returns (there is no pending work left to abandon).
755
+ // Reset afterwards so stop() is idempotent: close() calls stop() from the destructor and the user
756
+ // may also call it explicitly, and a joined pool must not be joined again.
757
+ if (atr_cleanup_pool_) {
758
+ atr_cleanup_pool_->stop();
759
+ atr_cleanup_pool_->join();
760
+ atr_cleanup_pool_.reset();
761
+ }
672
762
  }
673
763
 
674
764
  void
@@ -676,7 +766,10 @@ transactions_cleanup::close()
676
766
  {
677
767
  stop();
678
768
  CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG("all lost attempt cleanup threads closed");
679
- remove_client_record_from_all_buckets(client_uuid_);
769
+ // In a forked child the record is the parent's, and the parent is still registered under it.
770
+ if (client_record_owner_pid_ == utils::current_process_id()) {
771
+ remove_client_record_from_all_buckets(client_uuid_);
772
+ }
680
773
  }
681
774
 
682
775
  transactions_cleanup::~transactions_cleanup()
@@ -32,17 +32,6 @@ transactions_config::transactions_config()
32
32
 
33
33
  transactions_config::~transactions_config() = default;
34
34
 
35
- transactions_config::transactions_config(transactions_config&& c) noexcept
36
- : level_(c.level_)
37
- , timeout_(c.timeout_)
38
- , attempt_context_hooks_(c.attempt_context_hooks_)
39
- , cleanup_hooks_(c.cleanup_hooks_)
40
- , metadata_collection_(std::move(c.metadata_collection_))
41
- , query_config_(c.query_config_)
42
- , cleanup_config_(std::move(c.cleanup_config_))
43
- {
44
- }
45
-
46
35
  transactions_config::transactions_config(const transactions_config& config)
47
36
  : level_(config.durability_level())
48
37
  , timeout_(config.timeout())
@@ -20,6 +20,7 @@
20
20
  #include "core/operations/document_lookup_in.hxx"
21
21
  #include "core/operations/management/bucket_get_all.hxx"
22
22
  #include "core/transactions/error_class.hxx"
23
+ #include "core/utils/byteswap.hxx"
23
24
 
24
25
  #include <couchbase/collection.hxx>
25
26
  #include <couchbase/scope.hxx>
@@ -53,6 +54,15 @@ now_ns_from_vbucket(const tao::json::value& vbucket) -> std::uint64_t
53
54
  return stoull(now_str, nullptr, 10) * 1000000000;
54
55
  }
55
56
 
57
+ auto
58
+ parse_mutation_cas(const std::string& cas) -> std::uint64_t
59
+ {
60
+ if (cas.empty()) {
61
+ return 0;
62
+ }
63
+ return core::utils::reverse_bytes(std::stoull(cas, nullptr, 16)) / 1000000;
64
+ }
65
+
56
66
  void
57
67
  wrap_collection_call(result& res, const std::function<void(result&)>& call)
58
68
  {
@@ -169,7 +169,7 @@ public:
169
169
  }
170
170
  void decrement_in_flight()
171
171
  {
172
- std::lock_guard<std::mutex> lock(mutex_);
172
+ std::scoped_lock<std::mutex> lock(mutex_);
173
173
  in_flight_--;
174
174
  CB_TXN_LOG_TRACE("in_flight decremented to {}", in_flight_);
175
175
  assert(in_flight_ >= 0);
@@ -181,7 +181,7 @@ public:
181
181
  private:
182
182
  void change_count(int32_t val)
183
183
  {
184
- std::lock_guard<std::mutex> lock(mutex_);
184
+ std::scoped_lock<std::mutex> lock(mutex_);
185
185
  if (allow_ops_) {
186
186
  count_ += val;
187
187
  if (val > 0) {
@@ -127,7 +127,7 @@ using txn_complete_callback =
127
127
  /** @brief Main class for creating a transaction
128
128
  *
129
129
  */
130
- class transactions : public couchbase::transactions::transactions
130
+ class transactions
131
131
  {
132
132
  public:
133
133
  /**
@@ -182,10 +182,6 @@ public:
182
182
  auto run(const couchbase::transactions::transaction_options& config,
183
183
  logic&& code) -> couchbase::transactions::transaction_result;
184
184
 
185
- auto run(::couchbase::transactions::txn_logic&& code,
186
- const couchbase::transactions::transaction_options& config)
187
- -> std::pair<couchbase::error, couchbase::transactions::transaction_result> override;
188
-
189
185
  /**
190
186
  * @brief Run a transaction
191
187
  *
@@ -204,9 +200,6 @@ public:
204
200
  async_logic&& code,
205
201
  txn_complete_callback&& cb);
206
202
 
207
- void run(couchbase::transactions::async_txn_logic&& code,
208
- couchbase::transactions::async_txn_complete_logic&& complete_cb,
209
- const couchbase::transactions::transaction_options& config) override;
210
203
  /**
211
204
  * @internal
212
205
  * called internally - will likely move
@@ -279,7 +272,7 @@ public:
279
272
  transactions(core::cluster cluster, couchbase::transactions::transactions_config::built config);
280
273
  transactions(core::cluster cluster, const couchbase::transactions::transactions_config& config);
281
274
 
282
- ~transactions() override;
275
+ ~transactions();
283
276
 
284
277
  private:
285
278
  core::cluster cluster_;
@@ -288,5 +281,12 @@ private:
288
281
  const std::size_t max_attempts_{ 1000 };
289
282
  const std::chrono::milliseconds min_retry_delay_{ 1 };
290
283
  };
284
+
285
+ // TODO(DC): Can we replace this with something better?
286
+ // This is needed as the performer needs to access the Core API to set hooks, access cleanup-related
287
+ // metrics, which are not exposed in the Public API.
288
+ auto
289
+ get_core_transactions(const std::shared_ptr<couchbase::transactions::transactions>& transactions)
290
+ -> std::shared_ptr<couchbase::core::transactions::transactions>;
291
291
  } // namespace transactions
292
292
  } // namespace couchbase::core