couchbase 3.8.1 → 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 (475) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  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 +214 -155
  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 +325 -25
  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 +66 -0
  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 +316 -317
  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 +275 -14
  131. data/ext/couchbase/core/io/streams.hxx +53 -0
  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/bucket_create.cxx +37 -38
  183. data/ext/couchbase/core/operations/management/bucket_update.cxx +26 -28
  184. data/ext/couchbase/core/operations/management/change_password.cxx +2 -0
  185. data/ext/couchbase/core/operations/management/collection_drop.cxx +5 -1
  186. data/ext/couchbase/core/operations/management/error_utils.cxx +59 -5
  187. data/ext/couchbase/core/operations/management/error_utils.hxx +29 -1
  188. data/ext/couchbase/core/operations/management/eventing_deploy_function.cxx +9 -3
  189. data/ext/couchbase/core/operations/management/eventing_drop_function.cxx +8 -3
  190. data/ext/couchbase/core/operations/management/eventing_get_all_functions.cxx +4 -2
  191. data/ext/couchbase/core/operations/management/eventing_get_function.cxx +5 -3
  192. data/ext/couchbase/core/operations/management/eventing_get_status.cxx +4 -2
  193. data/ext/couchbase/core/operations/management/eventing_pause_function.cxx +9 -3
  194. data/ext/couchbase/core/operations/management/eventing_resume_function.cxx +9 -3
  195. data/ext/couchbase/core/operations/management/eventing_undeploy_function.cxx +9 -3
  196. data/ext/couchbase/core/operations/management/eventing_upsert_function.cxx +9 -3
  197. data/ext/couchbase/core/operations/management/group_drop.cxx +3 -1
  198. data/ext/couchbase/core/operations/management/group_get.cxx +3 -1
  199. data/ext/couchbase/core/operations/management/group_upsert.cxx +11 -8
  200. data/ext/couchbase/core/operations/management/query_index_build.cxx +5 -2
  201. data/ext/couchbase/core/operations/management/query_index_create.cxx +9 -3
  202. data/ext/couchbase/core/operations/management/query_index_drop.cxx +11 -4
  203. data/ext/couchbase/core/operations/management/query_index_get_all.cxx +1 -1
  204. data/ext/couchbase/core/operations/management/query_index_get_all_deferred.cxx +1 -1
  205. data/ext/couchbase/core/operations/management/scope_drop.cxx +5 -1
  206. data/ext/couchbase/core/operations/management/scope_get_all.cxx +6 -1
  207. data/ext/couchbase/core/operations/management/search_get_stats.cxx +10 -0
  208. data/ext/couchbase/core/operations/management/search_index_analyze_document.cxx +3 -2
  209. data/ext/couchbase/core/operations/management/search_index_control_ingest.cxx +4 -3
  210. data/ext/couchbase/core/operations/management/search_index_control_plan_freeze.cxx +4 -3
  211. data/ext/couchbase/core/operations/management/search_index_control_query.cxx +4 -3
  212. data/ext/couchbase/core/operations/management/search_index_drop.cxx +2 -2
  213. data/ext/couchbase/core/operations/management/search_index_get.cxx +2 -2
  214. data/ext/couchbase/core/operations/management/search_index_get_documents_count.cxx +3 -2
  215. data/ext/couchbase/core/operations/management/search_index_get_stats.cxx +3 -1
  216. data/ext/couchbase/core/operations/management/search_index_upsert.cxx +2 -2
  217. data/ext/couchbase/core/operations/management/user_drop.cxx +3 -1
  218. data/ext/couchbase/core/operations/management/user_get.cxx +3 -1
  219. data/ext/couchbase/core/operations/management/user_upsert.cxx +12 -9
  220. data/ext/couchbase/core/operations/management/view_index_drop.cxx +1 -1
  221. data/ext/couchbase/core/operations/management/view_index_get.cxx +1 -1
  222. data/ext/couchbase/core/operations/management/view_index_upsert.cxx +1 -1
  223. data/ext/couchbase/core/operations/operation_traits.hxx +18 -0
  224. data/ext/couchbase/core/operations/query_response_parsing.cxx +211 -0
  225. data/ext/couchbase/core/operations/query_response_parsing.hxx +60 -0
  226. data/ext/couchbase/core/operations_fwd.hxx +3 -3
  227. data/ext/couchbase/core/origin.cxx +54 -26
  228. data/ext/couchbase/core/origin.hxx +12 -0
  229. data/ext/couchbase/core/orphan_reporter.cxx +2 -1
  230. data/ext/couchbase/core/platform/random.cc +7 -7
  231. data/ext/couchbase/core/protocol/client_request.cxx +2 -2
  232. data/ext/couchbase/core/protocol/client_request.hxx +38 -21
  233. data/ext/couchbase/core/protocol/client_response.cxx +11 -3
  234. data/ext/couchbase/core/protocol/client_response.hxx +27 -6
  235. data/ext/couchbase/core/protocol/cmd_append.cxx +2 -2
  236. data/ext/couchbase/core/protocol/cmd_cluster_map_change_notification.cxx +2 -2
  237. data/ext/couchbase/core/protocol/cmd_decrement.cxx +6 -6
  238. data/ext/couchbase/core/protocol/cmd_get.cxx +1 -1
  239. data/ext/couchbase/core/protocol/cmd_get_and_lock.cxx +2 -2
  240. data/ext/couchbase/core/protocol/cmd_get_and_touch.cxx +2 -2
  241. data/ext/couchbase/core/protocol/cmd_get_cluster_config.cxx +6 -4
  242. data/ext/couchbase/core/protocol/cmd_get_collection_id.cxx +2 -2
  243. data/ext/couchbase/core/protocol/cmd_get_error_map.cxx +1 -1
  244. data/ext/couchbase/core/protocol/cmd_get_meta.cxx +4 -4
  245. data/ext/couchbase/core/protocol/cmd_get_replica.cxx +1 -1
  246. data/ext/couchbase/core/protocol/cmd_hello.cxx +1 -1
  247. data/ext/couchbase/core/protocol/cmd_increment.cxx +6 -6
  248. data/ext/couchbase/core/protocol/cmd_insert.cxx +4 -4
  249. data/ext/couchbase/core/protocol/cmd_lookup_in.cxx +4 -3
  250. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +4 -3
  251. data/ext/couchbase/core/protocol/cmd_mutate_in.cxx +10 -9
  252. data/ext/couchbase/core/protocol/cmd_mutate_in.hxx +10 -10
  253. data/ext/couchbase/core/protocol/cmd_observe_seqno.cxx +7 -7
  254. data/ext/couchbase/core/protocol/cmd_prepend.cxx +2 -2
  255. data/ext/couchbase/core/protocol/cmd_remove.cxx +2 -2
  256. data/ext/couchbase/core/protocol/cmd_replace.cxx +4 -4
  257. data/ext/couchbase/core/protocol/cmd_touch.cxx +1 -1
  258. data/ext/couchbase/core/protocol/cmd_upsert.cxx +4 -4
  259. data/ext/couchbase/core/protocol/datatype.hxx +118 -1
  260. data/ext/couchbase/core/protocol/frame_info_utils.cxx +1 -1
  261. data/ext/couchbase/core/protocol/magic.hxx +4 -0
  262. data/ext/couchbase/core/protocol/magic_fmt.hxx +3 -0
  263. data/ext/couchbase/core/protocol/server_request.hxx +3 -1
  264. data/ext/couchbase/core/protocol/status.cxx +1 -1
  265. data/ext/couchbase/core/protostellar/analytics_converter.hxx +171 -0
  266. data/ext/couchbase/core/protostellar/bucket_admin_converter.hxx +301 -0
  267. data/ext/couchbase/core/protostellar/collection_admin_converter.hxx +92 -0
  268. data/ext/couchbase/core/protostellar/component.cxx +2445 -0
  269. data/ext/couchbase/core/protostellar/component.hxx +391 -0
  270. data/ext/couchbase/core/{columnar/bootstrap_notification_subscriber.hxx → protostellar/compression_settings.hxx} +11 -10
  271. data/ext/couchbase/core/protostellar/credentials.cxx +233 -0
  272. data/ext/couchbase/core/protostellar/credentials.hxx +88 -0
  273. data/ext/couchbase/core/protostellar/dispatcher.cxx +57 -0
  274. data/ext/couchbase/core/protostellar/dispatcher.hxx +482 -0
  275. data/ext/couchbase/core/protostellar/error_utils.cxx +293 -0
  276. data/ext/couchbase/core/protostellar/error_utils.hxx +97 -0
  277. data/ext/couchbase/core/protostellar/json_payload.hxx +49 -0
  278. data/ext/couchbase/core/protostellar/kv_converter.cxx +70 -0
  279. data/ext/couchbase/core/protostellar/kv_converter.hxx +600 -0
  280. data/ext/couchbase/core/protostellar/query_converter.hxx +265 -0
  281. data/ext/couchbase/core/protostellar/query_index_admin_converter.hxx +291 -0
  282. data/ext/couchbase/core/protostellar/query_proto.hxx +73 -0
  283. data/ext/couchbase/core/protostellar/search_converter.hxx +244 -0
  284. data/ext/couchbase/core/protostellar/search_index_admin_converter.hxx +156 -0
  285. data/ext/couchbase/core/protostellar/view_converter.hxx +158 -0
  286. data/ext/couchbase/core/query_stream.cxx +447 -0
  287. data/ext/couchbase/core/query_stream.hxx +141 -0
  288. data/ext/couchbase/core/query_stream_component.cxx +243 -0
  289. data/ext/couchbase/core/query_stream_component.hxx +68 -0
  290. data/ext/couchbase/core/range_scan_load_balancer.cxx +15 -8
  291. data/ext/couchbase/core/range_scan_orchestrator.cxx +15 -3
  292. data/ext/couchbase/core/response_handler.hxx +41 -4
  293. data/ext/couchbase/core/row_streamer.cxx +332 -46
  294. data/ext/couchbase/core/row_streamer.hxx +31 -1
  295. data/ext/couchbase/core/row_streamer_options.hxx +55 -0
  296. data/ext/couchbase/core/sasl/client.h +3 -0
  297. data/ext/couchbase/core/search_scoring.hxx +66 -0
  298. data/ext/couchbase/core/stream_error_details.hxx +69 -0
  299. data/ext/couchbase/core/timeout_defaults.hxx +0 -1
  300. data/ext/couchbase/core/topology/capabilities.hxx +6 -0
  301. data/ext/couchbase/core/topology/capabilities_fmt.hxx +3 -0
  302. data/ext/couchbase/core/topology/configuration.cxx +21 -6
  303. data/ext/couchbase/core/topology/configuration.hxx +29 -8
  304. data/ext/couchbase/core/topology/configuration_json.hxx +3 -0
  305. data/ext/couchbase/core/topology/error_map_json.hxx +3 -1
  306. data/ext/couchbase/core/tracing/threshold_logging_tracer.cxx +70 -4
  307. data/ext/couchbase/core/tracing/threshold_logging_tracer.hxx +10 -0
  308. data/ext/couchbase/core/transactions/active_transaction_record.cxx +0 -34
  309. data/ext/couchbase/core/transactions/attempt_context_impl.cxx +362 -241
  310. data/ext/couchbase/core/transactions/attempt_context_impl.hxx +24 -3
  311. data/ext/couchbase/core/transactions/error_list.hxx +2 -2
  312. data/ext/couchbase/core/transactions/get_multi_fetch.hxx +160 -0
  313. data/ext/couchbase/core/transactions/get_multi_orchestrator.cxx +326 -125
  314. data/ext/couchbase/core/transactions/get_multi_transaction_id.hxx +62 -0
  315. data/ext/couchbase/core/transactions/internal/atr_entry.hxx +22 -2
  316. data/ext/couchbase/core/transactions/internal/transaction_context.hxx +2 -2
  317. data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +23 -1
  318. data/ext/couchbase/core/transactions/internal/utils.hxx +50 -14
  319. data/ext/couchbase/core/transactions/staged_mutation.cxx +23 -18
  320. data/ext/couchbase/core/transactions/staged_mutation.hxx +15 -4
  321. data/ext/couchbase/core/transactions/transaction_context.cxx +55 -40
  322. data/ext/couchbase/core/transactions/transaction_get_result.hxx +16 -0
  323. data/ext/couchbase/core/transactions/transactions.cxx +3 -55
  324. data/ext/couchbase/core/transactions/transactions_cleanup.cxx +194 -66
  325. data/ext/couchbase/core/transactions/transactions_config.cxx +0 -11
  326. data/ext/couchbase/core/transactions/utils.cxx +10 -0
  327. data/ext/couchbase/core/transactions/waitable_op_list.hxx +15 -4
  328. data/ext/couchbase/core/transactions.hxx +9 -9
  329. data/ext/couchbase/core/utils/byteswap.hxx +166 -9
  330. data/ext/couchbase/core/utils/connection_string.cxx +50 -85
  331. data/ext/couchbase/core/utils/connection_string.hxx +15 -0
  332. data/ext/couchbase/core/{columnar/backoff_calculator.hxx → utils/deadline.hxx} +13 -13
  333. data/ext/couchbase/core/utils/duration_parser.cxx +1 -1
  334. data/ext/couchbase/core/utils/json_streaming_lexer.cxx +48 -5
  335. data/ext/couchbase/core/utils/json_streaming_lexer.hxx +4 -1
  336. data/ext/couchbase/core/utils/movable_function.hxx +301 -52
  337. data/ext/couchbase/core/utils/name_codec.hxx +154 -11
  338. data/ext/couchbase/core/{columnar/retry_info.hxx → utils/process_id.hxx} +18 -13
  339. data/ext/couchbase/core/utils/url_codec.cxx +12 -51
  340. data/ext/couchbase/core/websocket_codec.cxx +3 -29
  341. data/ext/couchbase/couchbase/analytics_options.hxx +21 -10
  342. data/ext/couchbase/couchbase/analytics_row.hxx +105 -0
  343. data/ext/couchbase/couchbase/analytics_stream_result.hxx +266 -0
  344. data/ext/couchbase/couchbase/boolean_query.hxx +3 -3
  345. data/ext/couchbase/couchbase/bucket.hxx +57 -0
  346. data/ext/couchbase/couchbase/cluster.hxx +216 -5
  347. data/ext/couchbase/{core/columnar/security_options.hxx → couchbase/cluster_state.hxx} +18 -14
  348. data/ext/couchbase/couchbase/codec/lenient_json_transcoder.hxx +69 -0
  349. data/ext/couchbase/couchbase/collection.hxx +234 -14
  350. data/ext/couchbase/couchbase/common_durability_options.hxx +18 -3
  351. data/ext/couchbase/couchbase/conjunction_query.hxx +1 -1
  352. data/ext/couchbase/couchbase/create_dataset_analytics_options.hxx +6 -1
  353. data/ext/couchbase/couchbase/date_range_query.hxx +2 -2
  354. data/ext/couchbase/couchbase/decrement_options.hxx +1 -1
  355. data/ext/couchbase/couchbase/disjunction_query.hxx +1 -1
  356. data/ext/couchbase/couchbase/error.hxx +32 -0
  357. data/ext/couchbase/couchbase/error_codes.hxx +32 -0
  358. data/ext/couchbase/couchbase/geo_bounding_box_query.hxx +1 -1
  359. data/ext/couchbase/couchbase/geo_distance_query.hxx +1 -1
  360. data/ext/couchbase/couchbase/geo_polygon_query.hxx +1 -1
  361. data/ext/couchbase/couchbase/get_replica_options.hxx +68 -0
  362. data/ext/couchbase/couchbase/get_replica_result.hxx +5 -3
  363. data/ext/couchbase/couchbase/get_replica_strategy.hxx +161 -0
  364. data/ext/couchbase/couchbase/get_result.hxx +1 -0
  365. data/ext/couchbase/couchbase/increment_options.hxx +1 -1
  366. data/ext/couchbase/couchbase/insert_options.hxx +1 -1
  367. data/ext/couchbase/couchbase/match_phrase_query.hxx +1 -1
  368. data/ext/couchbase/couchbase/match_query.hxx +1 -1
  369. data/ext/couchbase/couchbase/mutate_in_options.hxx +3 -3
  370. data/ext/couchbase/couchbase/network_options.hxx +1 -1
  371. data/ext/couchbase/couchbase/node_id.hxx +42 -14
  372. data/ext/couchbase/couchbase/node_id_for_options.hxx +7 -1
  373. data/ext/couchbase/couchbase/node_ids_options.hxx +8 -2
  374. data/ext/couchbase/couchbase/numeric_range_query.hxx +1 -1
  375. data/ext/couchbase/couchbase/phrase_query.hxx +1 -1
  376. data/ext/couchbase/couchbase/prefix_query.hxx +1 -1
  377. data/ext/couchbase/couchbase/query_options.hxx +23 -12
  378. data/ext/couchbase/couchbase/query_row.hxx +111 -0
  379. data/ext/couchbase/couchbase/query_stream_result.hxx +302 -0
  380. data/ext/couchbase/couchbase/query_string_query.hxx +3 -3
  381. data/ext/couchbase/couchbase/regexp_query.hxx +1 -1
  382. data/ext/couchbase/couchbase/replace_options.hxx +3 -3
  383. data/ext/couchbase/couchbase/result.hxx +14 -3
  384. data/ext/couchbase/couchbase/scan_result_item.hxx +1 -0
  385. data/ext/couchbase/couchbase/scope.hxx +85 -2
  386. data/ext/couchbase/couchbase/search_options.hxx +58 -8
  387. data/ext/couchbase/couchbase/search_scoring.hxx +94 -0
  388. data/ext/couchbase/couchbase/search_scoring_none.hxx +51 -0
  389. data/ext/couchbase/couchbase/search_scoring_reciprocal_rank_fusion.hxx +88 -0
  390. data/ext/couchbase/couchbase/search_scoring_relative_score_fusion.hxx +70 -0
  391. data/ext/couchbase/couchbase/term_range_query.hxx +1 -1
  392. data/ext/couchbase/couchbase/tracing/request_span.hxx +46 -0
  393. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +23 -21
  394. data/ext/couchbase/couchbase/transactions/transaction_get_multi_result.hxx +24 -19
  395. data/ext/couchbase/couchbase/transactions/transaction_get_result.hxx +17 -3
  396. data/ext/couchbase/couchbase/transactions/transactions_config.hxx +4 -2
  397. data/ext/couchbase/couchbase/transactions.hxx +27 -18
  398. data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
  399. data/ext/couchbase/couchbase/vector_query.hxx +1 -1
  400. data/ext/couchbase/couchbase/wait_until_ready_options.hxx +111 -0
  401. data/ext/couchbase/couchbase/wildcard_query.hxx +1 -1
  402. data/ext/extconf.rb +15 -10
  403. data/ext/rcb_analytics.cxx +82 -102
  404. data/ext/rcb_backend.cxx +42 -36
  405. data/ext/rcb_backend.hxx +2 -0
  406. data/ext/rcb_buckets.cxx +47 -59
  407. data/ext/rcb_collections.cxx +42 -52
  408. data/ext/rcb_crud.cxx +345 -224
  409. data/ext/rcb_diagnostics.cxx +117 -27
  410. data/ext/rcb_exceptions.cxx +152 -99
  411. data/ext/rcb_exceptions.hxx +25 -0
  412. data/ext/rcb_extras.cxx +18 -19
  413. data/ext/rcb_hdr_histogram.cxx +19 -4
  414. data/ext/rcb_logger.cxx +202 -27
  415. data/ext/rcb_logger.hxx +12 -0
  416. data/ext/rcb_multi.cxx +40 -40
  417. data/ext/rcb_observability.cxx +7 -5
  418. data/ext/rcb_query.cxx +99 -142
  419. data/ext/rcb_range_scan.cxx +41 -35
  420. data/ext/rcb_search.cxx +98 -99
  421. data/ext/rcb_users.cxx +36 -56
  422. data/ext/rcb_utils.cxx +100 -37
  423. data/ext/rcb_utils.hxx +287 -33
  424. data/ext/rcb_version.cxx +1 -1
  425. data/ext/rcb_views.cxx +55 -51
  426. data/lib/active_support/cache/couchbase_store.rb +77 -12
  427. data/lib/couchbase/bucket.rb +20 -0
  428. data/lib/couchbase/cluster.rb +33 -14
  429. data/lib/couchbase/collection.rb +36 -1
  430. data/lib/couchbase/errors.rb +18 -0
  431. data/lib/couchbase/fork_hooks.rb +16 -7
  432. data/lib/couchbase/get_replica_strategy.rb +89 -0
  433. data/lib/couchbase/logger.rb +1 -1
  434. data/lib/couchbase/management/analytics_index_manager.rb +15 -15
  435. data/lib/couchbase/management/bucket_manager.rb +6 -6
  436. data/lib/couchbase/management/collection_manager.rb +6 -6
  437. data/lib/couchbase/management/query_index_manager.rb +7 -7
  438. data/lib/couchbase/management/search_index_manager.rb +13 -13
  439. data/lib/couchbase/management/user_manager.rb +10 -10
  440. data/lib/couchbase/management/view_index_manager.rb +5 -5
  441. data/lib/couchbase/options.rb +181 -44
  442. data/lib/couchbase/protostellar/bucket.rb +4 -0
  443. data/lib/couchbase/protostellar/cluster.rb +4 -0
  444. data/lib/couchbase/protostellar/collection.rb +4 -0
  445. data/lib/couchbase/protostellar/error_handling.rb +2 -1
  446. data/lib/couchbase/protostellar/request.rb +1 -0
  447. data/lib/couchbase/protostellar/request_generator/kv.rb +16 -2
  448. data/lib/couchbase/protostellar/request_generator/search.rb +17 -1
  449. data/lib/couchbase/protostellar/retry/orchestrator.rb +3 -2
  450. data/lib/couchbase/protostellar/retry/strategies/fail_fast.rb +33 -0
  451. data/lib/couchbase/protostellar/retry/strategies.rb +1 -0
  452. data/lib/couchbase/search_options.rb +95 -6
  453. data/lib/couchbase/version.rb +1 -1
  454. metadata +92 -33
  455. data/ext/0001-fix-build-for-mingw-w64-ucrt-x86_64-toolchain.patch +0 -40
  456. data/ext/couchbase/core/columnar/agent.hxx +0 -71
  457. data/ext/couchbase/core/columnar/agent_config.hxx +0 -59
  458. data/ext/couchbase/core/columnar/background_bootstrap_listener.hxx +0 -42
  459. data/ext/couchbase/core/columnar/backoff_calculator.cxx +0 -62
  460. data/ext/couchbase/core/columnar/columnar_agent.cxx +0 -147
  461. data/ext/couchbase/core/columnar/columnar_agent_config.cxx +0 -48
  462. data/ext/couchbase/core/columnar/columnar_error.cxx +0 -51
  463. data/ext/couchbase/core/columnar/database_management_options.hxx +0 -56
  464. data/ext/couchbase/core/columnar/error.hxx +0 -51
  465. data/ext/couchbase/core/columnar/error_codes.cxx +0 -107
  466. data/ext/couchbase/core/columnar/error_codes.hxx +0 -75
  467. data/ext/couchbase/core/columnar/management_component.cxx +0 -300
  468. data/ext/couchbase/core/columnar/management_component.hxx +0 -56
  469. data/ext/couchbase/core/columnar/query_component.cxx +0 -523
  470. data/ext/couchbase/core/columnar/query_component.hxx +0 -57
  471. data/ext/couchbase/core/columnar/query_options.hxx +0 -59
  472. data/ext/couchbase/core/columnar/query_result.cxx +0 -130
  473. data/ext/couchbase/core/columnar/query_result.hxx +0 -78
  474. data/ext/couchbase/core/io/config_tracker.cxx +0 -687
  475. 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,12 +27,18 @@
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>
38
+ #include <future>
39
+ #include <list>
35
40
  #include <memory>
41
+ #include <thread>
36
42
  #include <utility>
37
43
 
38
44
  namespace couchbase::core::transactions
@@ -52,34 +58,13 @@ transactions_cleanup::transactions_cleanup(
52
58
  : cluster_(std::move(cluster))
53
59
  , config_(std::move(config))
54
60
  , client_uuid_(uid_generator::next())
61
+ , client_record_owner_pid_(utils::current_process_id())
55
62
  {
56
63
  start();
57
64
  }
58
65
 
59
66
  namespace
60
67
  {
61
- /**
62
- * ${Mutation.CAS} is written by kvengine with
63
- * 'macroToString(htonll(info.cas))'. Discussed this with KV team and, though
64
- * there is consensus that this is off (htonll is definitely wrong, and a string
65
- * is an odd choice), there are clients (SyncGateway) that consume the current
66
- * string, so it can't be changed. Note that only little-endian servers are
67
- * supported for Couchbase, so the 8 byte long inside the string will always be
68
- * little-endian ordered.
69
- *
70
- * Looks like: "0x000058a71dd25c15"
71
- * Want: 0x155CD21DA7580000 (1539336197457313792 in base10, an epoch
72
- * time in millionths of a second)
73
- */
74
- auto
75
- parse_mutation_cas(const std::string& cas) -> std::uint64_t
76
- {
77
- if (cas.empty()) {
78
- return 0;
79
- }
80
- return core::utils::byte_swap(std::stoull(cas, nullptr, 16)) / 1000000;
81
- }
82
-
83
68
  // TODO(CXXCBC-549)
84
69
  const std::string CLIENT_RECORD_DOC_ID = "_txn:client-record"; // NOLINT(cert-err58-cpp)
85
70
  } // namespace
@@ -108,7 +93,7 @@ transactions_cleanup::clean_collection(
108
93
  // first make sure the collection is in the list
109
94
  while (is_running()) {
110
95
  {
111
- const std::lock_guard<std::mutex> lock(mutex_);
96
+ const std::scoped_lock<std::mutex> lock(mutex_);
112
97
  if (collections_.end() == std::find(collections_.begin(), collections_.end(), keyspace)) {
113
98
  CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG(
114
99
  "cleanup for {} ending, no longer in collection cleanup list", keyspace);
@@ -131,54 +116,125 @@ transactions_cleanup::clean_collection(
131
116
  all_atrs.size(),
132
117
  config_.cleanup_config.cleanup_window.count());
133
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
+
134
162
  for (auto it = all_atrs.begin() + details.index_of_this_client; it < all_atrs.end();
135
163
  it += details.num_active_clients) {
136
- auto atrs_left_for_this_client =
137
- std::distance(it, all_atrs.end()) /
138
- 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;
139
173
  const auto now = std::chrono::steady_clock::now();
140
174
  const auto elapsed_in_cleanup_window =
141
175
  std::chrono::duration_cast<std::chrono::microseconds>(now - start);
142
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.
143
179
  const auto budget_for_this_atr = std::chrono::microseconds(
144
180
  remaining_in_cleanup_window.count() /
145
181
  std::max<decltype(it)::difference_type>(1, atrs_left_for_this_client));
146
- // clean the ATR entry
147
- std::string atr_id = *it;
148
- if (!is_running()) {
149
- CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG("cleanup of {} complete", keyspace);
150
- return;
151
- }
152
-
153
- try {
154
- handle_atr_cleanup({ keyspace.bucket, keyspace.scope, keyspace.collection, atr_id });
155
- } catch (const std::exception& e) {
156
- CB_LOST_ATTEMPT_CLEANUP_LOG_ERROR(
157
- "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();
158
185
  }
159
186
 
160
- const auto atr_end = std::chrono::steady_clock::now();
161
- const auto atr_used = std::chrono::duration_cast<std::chrono::microseconds>(atr_end - now);
162
- const auto atr_left = budget_for_this_atr - atr_used;
163
-
164
- // Too verbose to log, but leaving here commented as it may be useful later for internal
165
- // debugging
166
- /*CB_LOST_ATTEMPT_CLEANUP_LOG_INFO("{} {} atrs_left_for_this_client={}
167
- elapsed_in_cleanup_window={}us " "remaining_in_cleanup_window={}us
168
- budget_for_this_atr={}us atr_used={}us atr_left={}us", bucket_name, atr_id,
169
- atrs_left_for_this_client, elapsed_in_cleanup_window.count(),
170
- remaining_in_cleanup_window.count(),
171
- budget_for_this_atr.count(),
172
- atr_used.count(),
173
- atr_left.count());*/
174
-
175
- if (atr_left.count() > 0 &&
176
- atr_left.count() < 1000000000) { // safety check protects against bugs
177
- if (!interruptable_wait(atr_left)) {
178
- 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;
179
221
  }
180
222
  }
181
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
+ }
182
238
  } catch (const std::exception& ex) {
183
239
  CB_LOST_ATTEMPT_CLEANUP_LOG_ERROR("cleanup failed with {}, trying again in 3 sec...",
184
240
  ex.what());
@@ -586,10 +642,17 @@ transactions_cleanup::add_collection(const couchbase::transactions::transaction_
586
642
  auto it = std::find(collections_.begin(), collections_.end(), keyspace);
587
643
  if (it == collections_.end()) {
588
644
  collections_.emplace_back(keyspace);
589
- // start cleaning right away
590
- lost_attempt_cleanup_workers_.emplace_back([this, keyspace = collections_.back()]() {
591
- this->clean_collection(keyspace);
592
- });
645
+ // Spawn only while running. stop() takes the worker handles out and joins them, so a
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.
650
+ if (running_) {
651
+ // start cleaning right away
652
+ lost_attempt_cleanup_workers_.emplace_back([this, keyspace = collections_.back()]() {
653
+ this->clean_collection(keyspace);
654
+ });
655
+ }
593
656
  }
594
657
  lock.unlock();
595
658
  CB_ATTEMPT_CLEANUP_LOG_DEBUG("added {} to lost transaction cleanup", keyspace);
@@ -599,8 +662,41 @@ transactions_cleanup::add_collection(const couchbase::transactions::transaction_
599
662
  void
600
663
  transactions_cleanup::start()
601
664
  {
602
- running_ =
603
- config_.cleanup_config.cleanup_client_attempts || config_.cleanup_config.cleanup_lost_attempts;
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
+
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.
682
+ const std::unique_lock<std::mutex> lock(mutex_);
683
+ running_ = config_.cleanup_config.cleanup_client_attempts ||
684
+ config_.cleanup_config.cleanup_lost_attempts;
685
+
686
+ if (config_.cleanup_config.cleanup_lost_attempts) {
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.
693
+ for (const auto& keyspace : collections_) {
694
+ lost_attempt_cleanup_workers_.emplace_back([this, keyspace]() {
695
+ this->clean_collection(keyspace);
696
+ });
697
+ }
698
+ }
699
+ }
604
700
  if (config_.cleanup_config.cleanup_client_attempts) {
605
701
  cleanup_thr_ = std::thread([this] {
606
702
  attempts_loop();
@@ -628,12 +724,41 @@ transactions_cleanup::stop()
628
724
  cleanup_thr_.join();
629
725
  CB_ATTEMPT_CLEANUP_LOG_DEBUG("cleanup attempt thread closed");
630
726
  }
631
- for (auto& t : lost_attempt_cleanup_workers_) {
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.
741
+ std::list<std::thread> workers;
742
+ {
743
+ const std::unique_lock<std::mutex> lock(mutex_);
744
+ workers.swap(lost_attempt_cleanup_workers_);
745
+ }
746
+ for (auto& t : workers) {
632
747
  CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG("shutting down all lost attempt threads...");
633
748
  if (t.joinable()) {
634
749
  t.join();
635
750
  }
636
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
+ }
637
762
  }
638
763
 
639
764
  void
@@ -641,7 +766,10 @@ transactions_cleanup::close()
641
766
  {
642
767
  stop();
643
768
  CB_LOST_ATTEMPT_CLEANUP_LOG_DEBUG("all lost attempt cleanup threads closed");
644
- 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
+ }
645
773
  }
646
774
 
647
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
  {
@@ -79,6 +79,15 @@ public:
79
79
  // we have the lock. Block all further ops
80
80
  allow_ops_ = false;
81
81
  }
82
+ // Peek at whether query mode has already been entered. Briefly acquires mutex_ but, unlike
83
+ // get_mode(), never waits on cv_query_ for the query node to be set, so it returns immediately.
84
+ // Used by defensive invariant checks: a KV mutation must never be staged once query mode is
85
+ // entered.
86
+ auto query_mode_entered() const -> bool
87
+ {
88
+ const std::scoped_lock lock(mutex_);
89
+ return mode_.mode == attempt_mode::modes::QUERY;
90
+ }
82
91
  auto get_mode() -> attempt_mode
83
92
  {
84
93
  std::unique_lock<std::mutex> lock(mutex_);
@@ -143,8 +152,10 @@ public:
143
152
  {
144
153
  // when begin work errors out, it is fatal, so reset to kv mode here, allowing
145
154
  // rollback to function properly.
146
- std::unique_lock<std::mutex> lock;
155
+ const std::scoped_lock lock(mutex_);
147
156
  mode_.mode = attempt_mode::modes::KV;
157
+ // query_node is intentionally left populated: a retry runs with a fresh waitable_op_list, and
158
+ // get_mode()'s wait predicate keys on query_node, so clearing it here would serve no purpose.
148
159
  cv_query_.notify_all();
149
160
  }
150
161
 
@@ -158,7 +169,7 @@ public:
158
169
  }
159
170
  void decrement_in_flight()
160
171
  {
161
- std::lock_guard<std::mutex> lock(mutex_);
172
+ std::scoped_lock<std::mutex> lock(mutex_);
162
173
  in_flight_--;
163
174
  CB_TXN_LOG_TRACE("in_flight decremented to {}", in_flight_);
164
175
  assert(in_flight_ >= 0);
@@ -170,7 +181,7 @@ public:
170
181
  private:
171
182
  void change_count(int32_t val)
172
183
  {
173
- std::lock_guard<std::mutex> lock(mutex_);
184
+ std::scoped_lock<std::mutex> lock(mutex_);
174
185
  if (allow_ops_) {
175
186
  count_ += val;
176
187
  if (val > 0) {
@@ -199,6 +210,6 @@ private:
199
210
  std::condition_variable cv_ops_;
200
211
  std::condition_variable cv_query_;
201
212
  std::condition_variable cv_in_flight_;
202
- std::mutex mutex_;
213
+ mutable std::mutex mutex_;
203
214
  };
204
215
  } // namespace couchbase::core::transactions
@@ -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