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
@@ -49,11 +49,7 @@ public:
49
49
  std::shared_ptr<retry_strategy> default_retry_strategy = {});
50
50
 
51
51
  auto do_http_request(const http_request& request, free_form_http_request_callback&& callback)
52
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
53
- -> tl::expected<std::shared_ptr<pending_operation>, error_union>;
54
- #else
55
52
  -> tl::expected<std::shared_ptr<pending_operation>, std::error_code>;
56
- #endif
57
53
 
58
54
  auto do_http_request_buffered(const http_request& request,
59
55
  buffered_free_form_http_request_callback&& callback)
@@ -18,29 +18,36 @@
18
18
  #include "analytics.hxx"
19
19
 
20
20
  #include "core/analytics_scan_consistency.hxx"
21
+ #include "core/analytics_stream.hxx"
22
+ #include "core/cluster.hxx"
21
23
  #include "core/operations/document_analytics.hxx"
22
24
  #include "core/utils/binary.hxx"
25
+ #include "error.hxx"
26
+ #include "internal_analytics_stream_result.hxx"
27
+ #include "observability_recorder.hxx"
23
28
 
24
29
  #include <couchbase/analytics_metrics.hxx>
25
30
  #include <couchbase/analytics_options.hxx>
26
31
  #include <couchbase/analytics_result.hxx>
27
32
  #include <couchbase/analytics_scan_consistency.hxx>
28
33
  #include <couchbase/analytics_status.hxx>
34
+ #include <couchbase/analytics_stream_result.hxx>
29
35
  #include <couchbase/analytics_warning.hxx>
30
36
  #include <couchbase/codec/encoded_value.hxx>
37
+ #include <couchbase/error.hxx>
31
38
 
32
39
  #include <cstddef>
40
+ #include <memory>
33
41
  #include <optional>
42
+ #include <system_error>
34
43
  #include <utility>
35
44
  #include <vector>
36
45
 
37
46
  namespace couchbase::core::impl
38
47
  {
39
- namespace
40
- {
41
-
42
48
  auto
43
- map_status(core::operations::analytics_response::analytics_status status) -> analytics_status
49
+ map_analytics_status(core::operations::analytics_response::analytics_status status)
50
+ -> analytics_status
44
51
  {
45
52
  switch (status) {
46
53
  case core::operations::analytics_response::running:
@@ -67,6 +74,9 @@ map_status(core::operations::analytics_response::analytics_status status) -> ana
67
74
  return analytics_status::unknown;
68
75
  }
69
76
 
77
+ namespace
78
+ {
79
+
70
80
  auto
71
81
  map_scan_consistency(std::optional<couchbase::analytics_scan_consistency> consistency)
72
82
  -> std::optional<couchbase::core::analytics_scan_consistency>
@@ -136,7 +146,7 @@ build_result(core::operations::analytics_response& resp) -> analytics_result
136
146
  analytics_meta_data{
137
147
  std::move(resp.meta.request_id),
138
148
  std::move(resp.meta.client_context_id),
139
- map_status(resp.meta.status),
149
+ map_analytics_status(resp.meta.status),
140
150
  map_warnings(resp),
141
151
  map_metrics(resp),
142
152
  map_signature(resp),
@@ -145,6 +155,29 @@ build_result(core::operations::analytics_response& resp) -> analytics_result
145
155
  };
146
156
  }
147
157
 
158
+ void
159
+ dispatch_analytics_stream(const core::cluster& core,
160
+ core::operations::analytics_request request,
161
+ std::unique_ptr<observability_recorder> obs_rec,
162
+ analytics_stream_handler&& handler)
163
+ {
164
+ core.analytics_query_stream(
165
+ std::move(request),
166
+ [obs_rec = std::move(obs_rec), handler = std::move(handler)](
167
+ core::analytics_stream stream, core::error_context::analytics ctx) mutable {
168
+ if (ctx.ec) {
169
+ obs_rec->finish(ctx.ec);
170
+ return handler(make_error(ctx), {});
171
+ }
172
+ // Transfer the recorder to the handle; it finishes the operation at the stream terminal. The
173
+ // context travels with it so a terminal error is reported with the same statement/endpoint
174
+ // detail as a failure to start, plus whatever the trailer said.
175
+ auto internal = std::make_shared<internal_analytics_stream_result>(
176
+ std::move(stream), std::move(obs_rec), std::move(ctx));
177
+ handler({}, analytics_stream_result{ std::move(internal) });
178
+ });
179
+ }
180
+
148
181
  auto
149
182
  build_analytics_request(std::string statement,
150
183
  analytics_options::built options,
@@ -20,15 +20,49 @@
20
20
  #include "core/operations/document_analytics.hxx"
21
21
 
22
22
  #include <couchbase/analytics_options.hxx>
23
+ #include <couchbase/analytics_status.hxx>
23
24
 
25
+ #include <memory>
24
26
  #include <optional>
25
27
  #include <string>
26
28
 
29
+ namespace couchbase::core
30
+ {
31
+ class cluster;
32
+ } // namespace couchbase::core
33
+
27
34
  namespace couchbase::core::impl
28
35
  {
36
+ class observability_recorder;
37
+
38
+ /**
39
+ * Maps the core analytics status enum onto the public analytics_status enum. Shared by the buffered
40
+ * result builder and the streaming result handle so there is one source of truth.
41
+ */
42
+ auto
43
+ map_analytics_status(core::operations::analytics_response::analytics_status status)
44
+ -> analytics_status;
45
+
29
46
  auto
30
47
  build_result(core::operations::analytics_response& resp) -> analytics_result;
31
48
 
49
+ /**
50
+ * Dispatches an analytics query as a streaming request and resolves the handler with an
51
+ * analytics_stream_result. Unlike query there is no prepared-statement fallback; every request
52
+ * takes the lazy streaming path.
53
+ *
54
+ * @param obs_rec the operation's observability recorder. Its operation span is already threaded
55
+ * into request.parent_span by the caller; ownership is transferred to the resulting stream handle,
56
+ * which calls finish() once when the stream reaches its terminal (drained, errored, or cancelled),
57
+ * so the streaming path emits the same operation span + latency metric as buffered
58
+ * analytics_query().
59
+ */
60
+ void
61
+ dispatch_analytics_stream(const core::cluster& core,
62
+ core::operations::analytics_request request,
63
+ std::unique_ptr<observability_recorder> obs_rec,
64
+ analytics_stream_handler&& handler);
65
+
32
66
  auto
33
67
  build_analytics_request(std::string statement,
34
68
  analytics_options::built options,
@@ -20,7 +20,6 @@
20
20
  #include <optional>
21
21
  #include <string>
22
22
  #include <system_error>
23
- #include <variant>
24
23
 
25
24
  namespace couchbase::core
26
25
  {
@@ -37,6 +36,4 @@ struct bootstrap_error {
37
36
 
38
37
  } // namespace impl
39
38
 
40
- using error_union = std::variant<std::monostate, std::error_code, impl::bootstrap_error>;
41
-
42
39
  } // namespace couchbase::core
@@ -24,12 +24,15 @@
24
24
  #include "invoke_with_node_id.hxx"
25
25
  #include "observability_recorder.hxx"
26
26
  #include "observe_poll.hxx"
27
+ #include "resolve_node_id.hxx"
28
+ #include "with_bucket_config_or_timeout.hxx"
27
29
 
28
30
  #include "core/agent_group.hxx"
29
31
  #include "core/agent_group_config.hxx"
30
32
  #include "core/cluster.hxx"
31
33
  #include "core/impl/subdoc/command.hxx"
32
34
  #include "core/logger/logger.hxx"
35
+ #include "core/logger/redaction.hxx"
33
36
  #include "core/metrics/meter_wrapper.hxx"
34
37
  #include "core/operations/document_append.hxx"
35
38
  #include "core/operations/document_decrement.hxx"
@@ -98,6 +101,7 @@
98
101
  #include <spdlog/fmt/bundled/core.h>
99
102
 
100
103
  #include <asio/error.hpp>
104
+ #include <asio/post.hpp>
101
105
  #include <asio/steady_timer.hpp>
102
106
 
103
107
  #include <atomic>
@@ -328,6 +332,51 @@ public:
328
332
  });
329
333
  }
330
334
 
335
+ void get_replica(std::string document_key,
336
+ const get_replica_strategy::built& strategy,
337
+ const get_replica_options::built& options,
338
+ core::operations::movable_get_replica_handler&& handler) const
339
+ {
340
+ auto obs_rec = create_observability_recorder(core::tracing::operation::mcbp_get_replica,
341
+ options.parent_span);
342
+
343
+ core::operations::get_replica_request request{
344
+ core::document_id{
345
+ bucket_name_,
346
+ scope_name_,
347
+ name_,
348
+ std::move(document_key),
349
+ },
350
+ options.timeout,
351
+ {},
352
+ {},
353
+ { options.retry_strategy },
354
+ obs_rec->operation_span(),
355
+ // The public strategy is translated here, so core carries no public type.
356
+ core::impl::replica_selection{ strategy.replica_index, strategy.wrap },
357
+ };
358
+ return core_.execute(std::move(request),
359
+ [obs_rec = std::move(obs_rec),
360
+ crypto_manager = crypto_manager_,
361
+ handler = std::move(handler)](auto resp) mutable {
362
+ // A document the replica does not hold reaches the
363
+ // caller as document_not_found_on_replica, so that
364
+ // is the error this operation is observed to have
365
+ // produced. resp.ctx.ec() still carries the
366
+ // document_not_found underneath it.
367
+ auto error = core::impl::make_get_replica_error(resp.ctx);
368
+ obs_rec->finish(resp.ctx.retry_attempts(), error.ec());
369
+ invoke_with_node_id(std::move(handler),
370
+ std::move(error),
371
+ get_replica_result{
372
+ resp.cas,
373
+ true,
374
+ { std::move(resp.value), resp.flags },
375
+ std::move(crypto_manager),
376
+ });
377
+ });
378
+ }
379
+
331
380
  void get_all_replicas(std::string document_key,
332
381
  const get_all_replicas_options::built& options,
333
382
  core::impl::movable_get_all_replicas_handler&& handler) const
@@ -346,25 +395,25 @@ public:
346
395
  options.read_preference,
347
396
  obs_rec->operation_span(),
348
397
  };
349
- return core_.execute(std::move(request),
350
- [obs_rec = std::move(obs_rec),
351
- crypto_manager = crypto_manager_,
352
- handler = std::move(handler)](auto resp) mutable {
353
- get_all_replicas_result result{};
354
- for (auto& entry : resp.entries) {
355
- get_replica_result replica_result{
356
- entry.cas,
357
- entry.replica,
358
- { std::move(entry.value), entry.flags },
359
- crypto_manager,
360
- };
361
- replica_result.node_id(std::move(entry.dispatched_to_node_id));
362
- result.emplace_back(std::move(replica_result));
363
- }
364
- obs_rec->finish(resp.ctx.ec());
365
- return handler(core::impl::make_error(std::move(resp.ctx)),
366
- std::move(result));
367
- });
398
+ return core_.execute(
399
+ std::move(request),
400
+ [obs_rec = std::move(obs_rec),
401
+ crypto_manager = crypto_manager_,
402
+ handler = std::move(handler)](auto resp) mutable {
403
+ get_all_replicas_result result{};
404
+ for (auto& entry : resp.entries) {
405
+ get_replica_result replica_result{
406
+ entry.cas,
407
+ entry.replica,
408
+ { std::move(entry.value), entry.flags },
409
+ crypto_manager,
410
+ };
411
+ replica_result.set_node_id_internal(std::move(entry.dispatched_to_node_id));
412
+ result.emplace_back(std::move(replica_result));
413
+ }
414
+ obs_rec->finish(resp.ctx.ec());
415
+ return handler(core::impl::make_error(std::move(resp.ctx)), std::move(result));
416
+ });
368
417
  }
369
418
 
370
419
  void remove(std::string document_key,
@@ -578,11 +627,8 @@ public:
578
627
  [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) mutable {
579
628
  obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec());
580
629
 
581
- if (resp.ctx.ec()) {
582
- invoke_with_node_id(
583
- std::move(handler), core::impl::make_error(std::move(resp.ctx)), lookup_in_result{});
584
- return;
585
- }
630
+ // Don't short-circuit on ctx.ec(): document-level success with per-path failures is
631
+ // normal for subdoc; fields is empty on document-level error so the loop is a no-op.
586
632
 
587
633
  std::vector<lookup_in_result::entry> entries{};
588
634
  entries.reserve(resp.fields.size());
@@ -643,7 +689,7 @@ public:
643
689
  res.deleted,
644
690
  res.is_replica,
645
691
  };
646
- replica_result.node_id(std::move(res.dispatched_to_node_id));
692
+ replica_result.set_node_id_internal(std::move(res.dispatched_to_node_id));
647
693
  result.emplace_back(std::move(replica_result));
648
694
  }
649
695
  obs_rec->finish(resp.ctx.ec());
@@ -1157,86 +1203,91 @@ public:
1157
1203
  break;
1158
1204
  }
1159
1205
 
1160
- return core_.open_bucket(
1206
+ // Fetch the bucket configuration through the shared async scaffold rather than nesting
1207
+ // open_bucket + with_bucket_configuration directly: the latter parks its continuation in the
1208
+ // core (in the bucket-bootstrap window it is not drained on close), so capturing the moved
1209
+ // scan_handler there would strand it -- a std::future caller would observe broken_promise and a
1210
+ // callback caller would hang -- if the cluster is torn down mid-scan. The scaffold anchors the
1211
+ // wait on an io_context timer and fails closed with errc::network::cluster_closed. Everything
1212
+ // the continuation needs is captured by value (not `this`): the scan handler outlives
1213
+ // collection_impl when the caller drops the collection but keeps the future, so a raw-`this`
1214
+ // capture would be a use-after-free.
1215
+ core::impl::with_bucket_config_or_timeout<std::shared_ptr<core::topology::configuration>>(
1216
+ core_,
1161
1217
  bucket_name_,
1162
- [this,
1163
- obs_rec = std::move(obs_rec),
1164
- handler = std::move(handler),
1218
+ orchestrator_opts.timeout,
1219
+ [core = core_,
1220
+ bucket_name = bucket_name_,
1221
+ scope_name = scope_name_,
1222
+ name = name_,
1223
+ crypto_manager = crypto_manager_,
1165
1224
  orchestrator_opts,
1166
- core_scan_type](std::error_code ec) mutable {
1167
- if (ec) {
1225
+ core_scan_type,
1226
+ obs_rec = std::move(obs_rec),
1227
+ handler = std::move(handler)](
1228
+ couchbase::error err, std::shared_ptr<core::topology::configuration> config) mutable {
1229
+ if (err.ec()) {
1230
+ obs_rec->finish(err.ec());
1231
+ return handler(
1232
+ error(err.ec(), "An error occurred when attempting to fetch the bucket configuration."),
1233
+ {});
1234
+ }
1235
+ if (!config->capabilities.supports_range_scan()) {
1236
+ obs_rec->finish(errc::common::feature_not_available);
1237
+ return handler(
1238
+ error(errc::common::feature_not_available, "This bucket does not support range scan."),
1239
+ {});
1240
+ }
1241
+ auto agent_group =
1242
+ core::agent_group(core.io_context(), core::agent_group_config{ { core } });
1243
+ if (auto ec = agent_group.open_bucket(bucket_name); ec) {
1168
1244
  obs_rec->finish(ec);
1169
- return handler(error(ec), {});
1245
+ return handler(
1246
+ error(ec, fmt::format("An error occurred while opening the `{}` bucket.", bucket_name)),
1247
+ {});
1248
+ }
1249
+ auto agent = agent_group.get_agent(bucket_name);
1250
+ if (!agent.has_value()) {
1251
+ obs_rec->finish(agent.error());
1252
+ return handler(
1253
+ error(
1254
+ agent.error(),
1255
+ fmt::format("An error occurred while getting an operation agent for the `{}` bucket.",
1256
+ bucket_name)),
1257
+ {});
1258
+ }
1259
+ if (!config->vbmap.has_value() || config->vbmap->empty()) {
1260
+ CB_LOG_WARNING("Unable to get vbucket map for `{}` - cannot perform scan operation",
1261
+ core::logger::metadata(bucket_name));
1262
+ obs_rec->finish(errc::common::request_canceled);
1263
+ return handler(error(errc::common::request_canceled,
1264
+ "No vbucket map included with the bucket config."),
1265
+ {});
1170
1266
  }
1171
- return core_.with_bucket_configuration(
1172
- bucket_name_,
1173
- [this,
1174
- obs_rec = std::move(obs_rec),
1175
- handler = std::move(handler),
1176
- orchestrator_opts,
1177
- core_scan_type](std::error_code ec,
1178
- const std::shared_ptr<core::topology::configuration>& config) mutable {
1179
- if (ec) {
1180
- obs_rec->finish(ec);
1181
- return handler(
1182
- error(ec, "An error occurred when attempting to fetch the bucket configuration."),
1183
- {});
1184
- }
1185
- if (!config->capabilities.supports_range_scan()) {
1186
- obs_rec->finish(ec);
1187
- return handler(error(errc::common::feature_not_available,
1188
- "This bucket does not support range scan."),
1189
- {});
1190
- }
1191
- auto agent_group =
1192
- core::agent_group(core_.io_context(), core::agent_group_config{ { core_ } });
1193
- ec = agent_group.open_bucket(bucket_name_);
1194
- if (ec) {
1195
- obs_rec->finish(ec);
1196
- return handler(error(ec,
1197
- fmt::format("An error occurred while opening the `{}` bucket.",
1198
- bucket_name_)),
1199
- {});
1200
- }
1201
- auto agent = agent_group.get_agent(bucket_name_);
1202
- if (!agent.has_value()) {
1203
- obs_rec->finish(ec);
1204
- return handler(
1205
- error(agent.error(),
1206
- fmt::format(
1207
- "An error occurred while getting an operation agent for the `{}` bucket",
1208
- bucket_name_)),
1209
- {});
1210
- }
1211
- if (!config->vbmap.has_value() || config->vbmap->empty()) {
1212
- CB_LOG_WARNING("Unable to get vbucket map for `{}` - cannot perform scan operation",
1213
- bucket_name_);
1214
- obs_rec->finish(ec);
1215
- return handler(error(errc::common::request_canceled,
1216
- "No vbucket map included with the bucket config"),
1217
- {});
1218
- }
1219
1267
 
1220
- auto orchestrator = core::range_scan_orchestrator(core_.io_context(),
1221
- agent.value(),
1222
- config->vbmap.value(),
1223
- scope_name_,
1224
- name_,
1225
- core_scan_type,
1226
- orchestrator_opts);
1227
- return orchestrator.scan(
1228
- [obs_rec = std::move(obs_rec),
1229
- crypto_manager = crypto_manager_,
1230
- handler = std::move(handler)](auto ec, auto core_scan_result) mutable {
1231
- obs_rec->finish(ec);
1232
- if (ec) {
1233
- return handler(error(ec, "Error while starting the range scan"), {});
1234
- }
1235
- auto internal_result = std::make_shared<internal_scan_result>(
1236
- std::move(core_scan_result), std::move(crypto_manager));
1237
- return handler({}, scan_result{ internal_result });
1238
- });
1239
- });
1268
+ auto orchestrator = core::range_scan_orchestrator(core.io_context(),
1269
+ agent.value(),
1270
+ config->vbmap.value(),
1271
+ scope_name,
1272
+ name,
1273
+ core_scan_type,
1274
+ orchestrator_opts);
1275
+ return orchestrator.scan([obs_rec = std::move(obs_rec),
1276
+ crypto_manager = std::move(crypto_manager),
1277
+ handler = std::move(handler)](auto ec,
1278
+ auto core_scan_result) mutable {
1279
+ obs_rec->finish(ec);
1280
+ if (ec) {
1281
+ return handler(error(ec, "Error while starting the range scan"), {});
1282
+ }
1283
+ auto internal_result = std::make_shared<internal_scan_result>(std::move(core_scan_result),
1284
+ std::move(crypto_manager));
1285
+ return handler({}, scan_result{ internal_result });
1286
+ });
1287
+ },
1288
+ [](const std::shared_ptr<core::topology::configuration>& config)
1289
+ -> std::pair<std::error_code, std::shared_ptr<core::topology::configuration>> {
1290
+ return { {}, config };
1240
1291
  });
1241
1292
  }
1242
1293
 
@@ -1248,66 +1299,29 @@ public:
1248
1299
  if (origin_ec) {
1249
1300
  return handler(error{ origin_ec }, node_id{});
1250
1301
  }
1251
- const bool is_tls = origin.options().enable_tls;
1252
- const auto timeout = options.timeout.value_or(origin.options().key_value_timeout);
1253
-
1254
- // Shared state so that only one of {timeout timer, bucket-config callback}
1255
- // can invoke the user handler. with_bucket_configuration() is not
1256
- // cancellable — if config never arrives, the timer is the only way to
1257
- // guarantee the handler completes.
1258
- struct state {
1259
- std::atomic<bool> done{ false };
1260
- asio::steady_timer timer;
1261
- node_id_for_handler handler;
1262
-
1263
- state(asio::io_context& ioc, node_id_for_handler&& h)
1264
- : timer{ ioc }
1265
- , handler{ std::move(h) }
1266
- {
1267
- }
1268
- };
1269
- auto shared = std::make_shared<state>(core_.io_context(), std::move(handler));
1302
+ // Validate the document key up front: an empty key would otherwise hash
1303
+ // to vbucket 0 unconditionally, producing a deterministic-but-meaningless
1304
+ // node_id. Surface this as invalid_argument rather than silently
1305
+ // succeeding with garbage.
1306
+ if (document_key.empty()) {
1307
+ return handler(error{ errc::common::invalid_argument }, node_id{});
1308
+ }
1270
1309
 
1271
- shared->timer.expires_after(timeout);
1272
- shared->timer.async_wait([shared](std::error_code timer_ec) -> void {
1273
- if (timer_ec == asio::error::operation_aborted) {
1274
- return;
1275
- }
1276
- if (shared->done.exchange(true)) {
1277
- return;
1278
- }
1279
- shared->handler(error{ errc::common::unambiguous_timeout }, node_id{});
1280
- });
1310
+ const auto transport_kind = origin.options().enable_tls ? core::topology::transport::tls
1311
+ : core::topology::transport::plain;
1312
+ const auto timeout = options.timeout.value_or(origin.options().key_value_timeout);
1281
1313
 
1282
- core_.with_bucket_configuration(
1314
+ // The state struct + timer + atomic-done + asio::post scaffold lives
1315
+ // in with_bucket_config_or_timeout(). See that header for the
1316
+ // single-threaded-io_context assumption the timer relies on.
1317
+ core::impl::with_bucket_config_or_timeout<couchbase::node_id>(
1318
+ core_,
1283
1319
  bucket_name_,
1284
- [shared, document_key = std::move(document_key), is_tls](
1285
- std::error_code ec, const std::shared_ptr<core::topology::configuration>& config) -> void {
1286
- if (shared->done.exchange(true)) {
1287
- return;
1288
- }
1289
- shared->timer.cancel();
1290
- if (ec) {
1291
- return shared->handler(error{ ec }, node_id{});
1292
- }
1293
- if (!config || !config->vbmap.has_value() || config->vbmap->empty()) {
1294
- return shared->handler(error{ errc::network::configuration_not_available }, node_id{});
1295
- }
1296
- // Only server_index is needed here; vbucket is intentionally unused.
1297
- // Binding it as [[maybe_unused]] would require C++20 structured-
1298
- // binding attribute support, so take the pair by value and pull the
1299
- // field we need instead.
1300
- const auto mapping = config->map_key(document_key, 0);
1301
- const auto& server_index = mapping.second;
1302
- if (!server_index.has_value() || server_index.value() >= config->nodes.size()) {
1303
- // An unresolvable server_index means the vBucket map is present
1304
- // but does not map this key to a known node (out-of-range or
1305
- // missing entry). Surface this as configuration_not_available
1306
- // rather than request_canceled so callers can distinguish an
1307
- // unusable map from an actual caller-initiated cancellation.
1308
- return shared->handler(error{ errc::network::configuration_not_available }, node_id{});
1309
- }
1310
- return shared->handler({}, config->nodes[server_index.value()].effective_node_id(is_tls));
1320
+ timeout,
1321
+ std::move(handler),
1322
+ [document_key = std::move(document_key),
1323
+ transport_kind](const std::shared_ptr<core::topology::configuration>& config) {
1324
+ return core::impl::resolve_node_id_from_config(config, document_key, transport_kind);
1311
1325
  });
1312
1326
  }
1313
1327
 
@@ -1317,54 +1331,29 @@ public:
1317
1331
  if (origin_ec) {
1318
1332
  return handler(error{ origin_ec }, {});
1319
1333
  }
1320
- const bool is_tls = origin.options().enable_tls;
1334
+ const auto transport_kind = origin.options().enable_tls ? core::topology::transport::tls
1335
+ : core::topology::transport::plain;
1321
1336
  const auto timeout = options.timeout.value_or(origin.options().key_value_timeout);
1322
1337
 
1323
- // Same shape as node_id_for: with_bucket_configuration() is not
1324
- // cancellable, so a steady_timer guarded by an atomic done-flag is
1325
- // the only way to bound the wait when no config arrives.
1326
- struct state {
1327
- std::atomic<bool> done{ false };
1328
- asio::steady_timer timer;
1329
- node_ids_handler handler;
1330
-
1331
- state(asio::io_context& ioc, node_ids_handler&& h)
1332
- : timer{ ioc }
1333
- , handler{ std::move(h) }
1334
- {
1335
- }
1336
- };
1337
- auto shared = std::make_shared<state>(core_.io_context(), std::move(handler));
1338
-
1339
- shared->timer.expires_after(timeout);
1340
- shared->timer.async_wait([shared](std::error_code timer_ec) -> void {
1341
- if (timer_ec == asio::error::operation_aborted) {
1342
- return;
1343
- }
1344
- if (shared->done.exchange(true)) {
1345
- return;
1346
- }
1347
- shared->handler(error{ errc::common::unambiguous_timeout }, {});
1348
- });
1349
-
1350
- core_.with_bucket_configuration(
1338
+ // Same async scaffold as node_id_for — see the long comment there for
1339
+ // the single-threaded-io_context assumption the timer relies on, and
1340
+ // for why with_bucket_configuration() needs a steady_timer to bound
1341
+ // the wait when no config ever arrives.
1342
+ //
1343
+ // Note: node_ids deliberately does not need to inspect the vBucket
1344
+ // map (effective_node_ids enumerates topology nodes directly, not via
1345
+ // a key->vbucket->node lookup), so the "vbmap missing" branch from
1346
+ // node_id_for is absent here. A null or empty result is collapsed
1347
+ // onto configuration_not_available below, matching the sibling's
1348
+ // contract that an empty/unusable bucket configuration is a hard
1349
+ // error rather than a silent empty success.
1350
+ core::impl::with_bucket_config_or_timeout<std::vector<couchbase::node_id>>(
1351
+ core_,
1351
1352
  bucket_name_,
1352
- [shared, is_tls](std::error_code ec,
1353
- const std::shared_ptr<core::topology::configuration>& config) -> void {
1354
- if (shared->done.exchange(true)) {
1355
- return;
1356
- }
1357
- shared->timer.cancel();
1358
- if (ec) {
1359
- return shared->handler(error{ ec }, {});
1360
- }
1361
- if (!config) {
1362
- // No config arrived but no error either — this should not
1363
- // happen in practice, but surface it the same way node_id_for
1364
- // does so callers can distinguish from request_canceled.
1365
- return shared->handler(error{ errc::network::configuration_not_available }, {});
1366
- }
1367
- return shared->handler({}, config->effective_node_ids(is_tls));
1353
+ timeout,
1354
+ std::move(handler),
1355
+ [transport_kind](const std::shared_ptr<core::topology::configuration>& config) {
1356
+ return core::impl::resolve_node_ids_from_config(config, transport_kind);
1368
1357
  });
1369
1358
  }
1370
1359
 
@@ -1636,6 +1625,30 @@ collection::get_any_replica(std::string document_id, const get_any_replica_optio
1636
1625
  return future;
1637
1626
  }
1638
1627
 
1628
+ void
1629
+ collection::get_replica(std::string document_id,
1630
+ const get_replica_strategy& strategy,
1631
+ const get_replica_options& options,
1632
+ get_replica_handler&& handler) const
1633
+ {
1634
+ return impl_->get_replica(
1635
+ std::move(document_id), strategy.build(), options.build(), std::move(handler));
1636
+ }
1637
+
1638
+ auto
1639
+ collection::get_replica(std::string document_id,
1640
+ const get_replica_strategy& strategy,
1641
+ const get_replica_options& options) const
1642
+ -> std::future<std::pair<error, get_replica_result>>
1643
+ {
1644
+ auto barrier = std::make_shared<std::promise<std::pair<error, get_replica_result>>>();
1645
+ auto future = barrier->get_future();
1646
+ get_replica(std::move(document_id), strategy, options, [barrier](auto err, auto result) {
1647
+ barrier->set_value({ std::move(err), std::move(result) });
1648
+ });
1649
+ return future;
1650
+ }
1651
+
1639
1652
  void
1640
1653
  collection::get_all_replicas(std::string document_id,
1641
1654
  const get_all_replicas_options& options,