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
@@ -84,7 +84,27 @@ public:
84
84
  watch_context(const watch_context&) = delete;
85
85
  auto operator=(const watch_context&) -> watch_context& = delete;
86
86
  auto operator=(watch_context&&) -> watch_context& = delete;
87
- ~watch_context() = default;
87
+
88
+ ~watch_context()
89
+ {
90
+ // Fail closed. During the polling-interval backoff the context is kept alive only by its own
91
+ // timer on the io_context; if the io_context is torn down then, that pending wait is dropped
92
+ // without running and the context is destroyed here with handler_ still set. Deliver an error
93
+ // so a std::future caller never observes broken_promise and a callback caller never hangs. (The
94
+ // GET-ALL request phase already fails closed -- core_.execute delivers request_canceled on
95
+ // close.) A moved-from context has an empty handler_, so this is a no-op there. Mirror
96
+ // finish(): move the handler out before invoking it and end the observability span, so the
97
+ // watch metrics are not left dangling on shutdown.
98
+ if (handler_) {
99
+ watch_query_indexes_handler handler{};
100
+ std::swap(handler, handler_);
101
+ try {
102
+ observability_recorder_->finish(errc::network::cluster_closed);
103
+ handler(couchbase::error{ errc::network::cluster_closed });
104
+ } catch (...) { // NOLINT(bugprone-empty-catch)
105
+ }
106
+ }
107
+ }
88
108
 
89
109
  void execute()
90
110
  {
@@ -412,6 +432,39 @@ public:
412
432
  collection_name,
413
433
  options.parent_span);
414
434
 
435
+ // A closed cluster is the only thing origin() reports an error for, and it cannot be asked
436
+ // which transport it is on. Report it here rather than routing on a state that could not be
437
+ // read: the composite below would open a sub-operation span for a request it never sends.
438
+ const auto [origin_ec, origin] = core_.origin();
439
+ if (origin_ec) {
440
+ top_obs_rec->finish(origin_ec);
441
+ return handler(couchbase::error{ origin_ec });
442
+ }
443
+
444
+ // Over couchbase2 the gateway does server-side what the two round trips below do here:
445
+ // BuildDeferredIndexes takes the keyspace and builds every deferred index in it. RFC 77 lists
446
+ // it among the single operations rather than the composite ones -- only WatchIndexes is
447
+ // composite there -- so it is dispatched as one request and carries no sub-operation spans.
448
+ if (origin.uses_protostellar()) {
449
+ core::operations::management::query_index_build_deferred_request request{
450
+ bucket_name,
451
+ scope_name.empty() ? std::optional<std::string>{}
452
+ : std::optional<std::string>{ scope_name },
453
+ collection_name.empty() ? std::optional<std::string>{}
454
+ : std::optional<std::string>{ collection_name },
455
+ {},
456
+ {},
457
+ timeout,
458
+ top_obs_rec->operation_span(),
459
+ };
460
+ return core_.execute(
461
+ std::move(request),
462
+ [top_obs_rec = std::move(top_obs_rec), handler = std::move(handler)](const auto& resp) {
463
+ top_obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec);
464
+ handler(core::impl::make_error(resp.ctx));
465
+ });
466
+ }
467
+
415
468
  auto get_all_deferred_obs_rec = create_suboperation_observability_recorder(
416
469
  core::tracing::operation::mgr_query_get_all_deferred_indexes,
417
470
  bucket_name,
@@ -18,10 +18,111 @@
18
18
 
19
19
  #include "replica_utils.hxx"
20
20
 
21
+ #include "core/impl/error.hxx"
21
22
  #include "core/logger/logger.hxx"
22
23
 
24
+ #include <couchbase/error_codes.hxx>
25
+
26
+ #include <cstddef>
27
+ #include <optional>
28
+ #include <utility>
29
+
23
30
  namespace couchbase::core::impl
24
31
  {
32
+ auto
33
+ make_get_replica_error(const key_value_error_context& ctx) -> couchbase::error
34
+ {
35
+ if (ctx.ec() != errc::key_value::document_not_found) {
36
+ return make_error(ctx);
37
+ }
38
+ auto cause = make_error(ctx);
39
+ auto error_context = cause.ctx();
40
+ return { errc::key_value::document_not_found_on_replica,
41
+ {},
42
+ std::move(error_context),
43
+ std::move(cause),
44
+ ctx.last_dispatched_to_node_id() };
45
+ }
46
+
47
+ auto
48
+ resolve_replica_index(const topology::configuration& config,
49
+ std::uint16_t vbucket,
50
+ std::size_t requested_replica,
51
+ bool wrap) -> replica_route_decision
52
+ {
53
+ replica_route_decision decision{};
54
+ decision.partition = vbucket;
55
+
56
+ if (!config.vbmap.has_value() || vbucket >= config.vbmap->size()) {
57
+ return decision;
58
+ }
59
+
60
+ if (!config.num_replicas.has_value()) {
61
+ // The configured count and the map are parsed independently, so a map can
62
+ // arrive without a count. Bounds cannot be decided without it, and taking
63
+ // the row as the count would route to a copy the bucket may not advertise,
64
+ // so this is treated the same as a map that has not arrived.
65
+ return decision;
66
+ }
67
+
68
+ // num_replicas bounds the index; the vbucket-map row bounds what is readable.
69
+ // A rebalance reshapes the rows after the new count is published, so a row
70
+ // can be shorter than the count. Such an index is valid and not yet readable,
71
+ // so it is not out of bounds.
72
+ const auto number_of_replicas = std::size_t{ *config.num_replicas };
73
+ if (number_of_replicas == 0) {
74
+ // This also keeps the wrap modulo below from dividing by zero.
75
+ decision.ec = errc::key_value::replica_index_out_of_bounds;
76
+ return decision;
77
+ }
78
+
79
+ // The row carries the active at position 0, so replica N is at N + 1 and the
80
+ // chain is one shorter than the row.
81
+ const auto& chain = config.vbmap->at(vbucket);
82
+ const auto chain_length = chain.empty() ? 0 : chain.size() - 1;
83
+
84
+ const auto readable = [&](std::size_t replica) -> std::optional<std::size_t> {
85
+ if (replica >= chain_length) {
86
+ return {};
87
+ }
88
+ // A -1 entry and a node index the topology does not list mean the same
89
+ // thing here: nothing to send to.
90
+ if (const auto server = config.server_by_vbucket(vbucket, replica + 1);
91
+ server.has_value() && server.value() < config.nodes.size()) {
92
+ return server;
93
+ }
94
+ return {};
95
+ };
96
+
97
+ if (!wrap) {
98
+ if (requested_replica >= number_of_replicas) {
99
+ decision.ec = errc::key_value::replica_index_out_of_bounds;
100
+ return decision;
101
+ }
102
+ if (const auto server = readable(requested_replica); server.has_value()) {
103
+ decision.replica_position = requested_replica + 1;
104
+ decision.server_index = server;
105
+ return decision;
106
+ }
107
+ decision.ec = errc::key_value::replica_index_currently_unavailable;
108
+ return decision;
109
+ }
110
+
111
+ // wrap walks num_replicas, not the row, so an index past a short row still
112
+ // reaches the replicas the row does list.
113
+ const auto start = requested_replica % number_of_replicas;
114
+ for (std::size_t offset = 0; offset < number_of_replicas; ++offset) {
115
+ const auto replica = (start + offset) % number_of_replicas;
116
+ if (const auto server = readable(replica); server.has_value()) {
117
+ decision.replica_position = replica + 1;
118
+ decision.server_index = server;
119
+ return decision;
120
+ }
121
+ }
122
+
123
+ decision.ec = errc::key_value::replica_index_currently_unavailable;
124
+ return decision;
125
+ }
25
126
 
26
127
  auto
27
128
  effective_nodes(const document_id& id,
@@ -19,12 +19,18 @@
19
19
  #pragma once
20
20
 
21
21
  #include "core/document_id.hxx"
22
+ #include "core/error_context/key_value_error_context.hxx"
22
23
  #include "core/topology/configuration.hxx"
23
24
 
25
+ #include "couchbase/error.hxx"
24
26
  #include "couchbase/read_preference.hxx"
25
27
 
28
+ #include <cstddef>
29
+ #include <cstdint>
26
30
  #include <memory>
31
+ #include <optional>
27
32
  #include <string>
33
+ #include <system_error>
28
34
  #include <vector>
29
35
 
30
36
  namespace couchbase::core::impl
@@ -34,6 +40,64 @@ struct readable_node {
34
40
  std::size_t index;
35
41
  };
36
42
 
43
+ /**
44
+ * What a targeted replica read asks of the routing. Translated out of the
45
+ * public strategy, so core does not depend on a public options header.
46
+ */
47
+ struct replica_selection {
48
+ std::size_t replica_index{};
49
+ bool wrap{};
50
+ };
51
+
52
+ /**
53
+ * Where a replica read should be sent, or why it cannot be sent at all.
54
+ *
55
+ * Exactly one of three states:
56
+ *
57
+ * - @c ec set: terminal, and completes the operation.
58
+ * - @c ec clear and @c server_index unset: the current topology cannot route
59
+ * the request, so the caller takes its retry path.
60
+ * - @c server_index set: the node to dispatch to.
61
+ */
62
+ struct replica_route_decision {
63
+ std::error_code ec{};
64
+ std::uint16_t partition{};
65
+ std::size_t replica_position{};
66
+ std::optional<std::size_t> server_index{};
67
+ };
68
+
69
+ /**
70
+ * Resolves a replica index, as the user requested it, against a vbucket map
71
+ * row.
72
+ *
73
+ * num_replicas bounds the index; the row bounds what is readable. A rebalance
74
+ * reshapes the rows after a new count is published, so the two can disagree.
75
+ *
76
+ * @param config current topology
77
+ * @param vbucket the vbucket the document belongs to
78
+ * @param requested_replica zero-based replica index, where 0 is the first
79
+ * replica and the active copy cannot be selected
80
+ * @param wrap walk on from an index that cannot be read to the next one that
81
+ * can, instead of failing
82
+ */
83
+ auto
84
+ resolve_replica_index(const topology::configuration& config,
85
+ std::uint16_t vbucket,
86
+ std::size_t requested_replica,
87
+ bool wrap) -> replica_route_decision;
88
+
89
+ /**
90
+ * Builds the error a replica read completes with.
91
+ *
92
+ * A document missing from the replica is reported as
93
+ * @ref errc::key_value::document_not_found_on_replica, with
94
+ * @ref errc::key_value::document_not_found as its cause so that a caller can
95
+ * opt into the general condition by reading the cause. Every other code is
96
+ * reported as it is.
97
+ */
98
+ auto
99
+ make_get_replica_error(const key_value_error_context& ctx) -> couchbase::error;
100
+
37
101
  /**
38
102
  * Returns list of server indexes to send operations. The index values are
39
103
  * in range [0, number_of_replicas).
@@ -0,0 +1,125 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2026-Present Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #pragma once
19
+
20
+ #include "core/topology/configuration.hxx"
21
+
22
+ #include <couchbase/error_codes.hxx>
23
+ #include <couchbase/node_id.hxx>
24
+
25
+ #include <memory>
26
+ #include <string>
27
+ #include <system_error>
28
+ #include <utility>
29
+ #include <vector>
30
+
31
+ namespace couchbase::core::impl
32
+ {
33
+ /**
34
+ * Pure helper that resolves a document key to the owning node's identity
35
+ * against a topology snapshot. Extracted out of @c collection_impl::node_id_for
36
+ * so the error-mapping branches (no config, no vbmap, out-of-range
37
+ * server_index, success) can be unit-tested without an io_context or a live
38
+ * cluster.
39
+ *
40
+ * @param config bucket configuration snapshot (may be null)
41
+ * @param document_key document key to map; empty input is reported as
42
+ * @c errc::common::invalid_argument
43
+ * @param t which port pair (plain/TLS) to use when deriving the
44
+ * node identity
45
+ * @return pair of (error_code, node_id). On success the @c error_code is
46
+ * default and the @c node_id is truthy; on failure the @c node_id
47
+ * is default-constructed (falsy).
48
+ */
49
+ [[nodiscard]] inline auto
50
+ resolve_node_id_from_config(const std::shared_ptr<topology::configuration>& config,
51
+ const std::string& document_key,
52
+ topology::transport t) -> std::pair<std::error_code, couchbase::node_id>
53
+ {
54
+ if (document_key.empty()) {
55
+ return { errc::common::invalid_argument, {} };
56
+ }
57
+ if (!config || !config->vbmap.has_value() || config->vbmap->empty()) {
58
+ return { errc::network::configuration_not_available, {} };
59
+ }
60
+ // Only the server_index half of the mapping is needed here; the vbucket id
61
+ // is intentionally discarded (avoids an unused structured-binding name).
62
+ const auto server_index = config->map_key(document_key, 0).second;
63
+ if (!server_index.has_value() || server_index.value() >= config->nodes.size()) {
64
+ // An unresolvable server_index means the vBucket map is present but does
65
+ // not map this key to a known node (out-of-range or missing entry).
66
+ // Surface this as configuration_not_available rather than
67
+ // request_canceled so callers can distinguish an unusable map from an
68
+ // actual caller-initiated cancellation.
69
+ return { errc::network::configuration_not_available, {} };
70
+ }
71
+ auto resolved = config->nodes[server_index.value()].effective_node_id(t);
72
+ if (!resolved) {
73
+ // The node maps to the key but carries no identity for the selected
74
+ // transport (e.g. no KV port for that port pair). Returning success here
75
+ // would violate the contract that a non-error result is always truthy,
76
+ // so report it as an unusable configuration instead.
77
+ return { errc::network::configuration_not_available, {} };
78
+ }
79
+ // Type the first element explicitly so the pair's perfect-forwarding
80
+ // constructor is selected and `resolved` is genuinely moved (a leading `{}`
81
+ // would bind the const-reference constructor instead, defeating the move).
82
+ return { std::error_code{}, std::move(resolved) };
83
+ }
84
+
85
+ /**
86
+ * Pure helper that enumerates the KV-serving nodes of a bucket configuration
87
+ * snapshot. Extracted out of @c collection_impl::node_ids so the
88
+ * error-mapping branches (no config, empty KV-serving set, success) can be
89
+ * unit-tested without an io_context or a live cluster — and so the branch
90
+ * matches the contract of the sibling resolver above:
91
+ *
92
+ * - A missing/null config snapshot is reported as
93
+ * @c errc::network::configuration_not_available (it must not be a silent
94
+ * empty success: the caller cannot distinguish "the cluster has zero KV
95
+ * nodes" from "we don't know the topology yet").
96
+ * - An empty effective-node-ids set is reported the same way; this is the
97
+ * mid-rebalance transient that the original commit silently surfaced as
98
+ * a successful empty vector.
99
+ *
100
+ * @param config bucket configuration snapshot (may be null)
101
+ * @param t which port pair (plain/TLS) to use when deriving identity
102
+ * @return pair of (error_code, vector<node_id>). On success the @c
103
+ * error_code is default and the vector is non-empty; on failure the
104
+ * vector is empty.
105
+ */
106
+ [[nodiscard]] inline auto
107
+ resolve_node_ids_from_config(const std::shared_ptr<topology::configuration>& config,
108
+ topology::transport t)
109
+ -> std::pair<std::error_code, std::vector<couchbase::node_id>>
110
+ {
111
+ if (!config) {
112
+ return { errc::network::configuration_not_available, {} };
113
+ }
114
+ auto nids = config->effective_node_ids(t);
115
+ if (nids.empty()) {
116
+ // Tighter than the original "return {}" — a momentarily-empty
117
+ // topology (e.g. mid-rebalance) is reported as unavailable rather
118
+ // than as a successful empty list, so registry-sweep loops keyed on
119
+ // node_id don't conclude the cluster has zero KV-serving nodes and
120
+ // discard every entry.
121
+ return { errc::network::configuration_not_available, {} };
122
+ }
123
+ return { {}, std::move(nids) };
124
+ }
125
+ } // namespace couchbase::core::impl
@@ -92,6 +92,22 @@ public:
92
92
  });
93
93
  }
94
94
 
95
+ void query_stream(std::string statement,
96
+ query_options::built options,
97
+ query_stream_handler&& handler) const
98
+ {
99
+ // Instrument the streaming operation exactly like the buffered scope query() above: build the
100
+ // recorder, thread its operation span into the request, and hand the recorder to the stream so
101
+ // it finishes at the terminal.
102
+ auto obs_rec = create_observability_recorder(
103
+ core::tracing::operation::query, core::service_type::query, options.parent_span);
104
+ obs_rec->with_query_statement(statement, options);
105
+ auto request = core::impl::build_query_request(
106
+ std::move(statement), query_context_, std::move(options), obs_rec->operation_span());
107
+ core::impl::dispatch_query_stream(
108
+ core_, std::move(request), std::move(obs_rec), std::move(handler));
109
+ }
110
+
95
111
  void analytics_query(std::string statement,
96
112
  analytics_options::built options,
97
113
  analytics_handler&& handler) const
@@ -111,6 +127,20 @@ public:
111
127
  });
112
128
  }
113
129
 
130
+ void analytics_query_stream(std::string statement,
131
+ analytics_options::built options,
132
+ analytics_stream_handler&& handler) const
133
+ {
134
+ // Instrument the streaming operation exactly like the buffered scope analytics_query() above.
135
+ auto obs_rec = create_observability_recorder(
136
+ core::tracing::operation::analytics, core::service_type::analytics, options.parent_span);
137
+ obs_rec->with_query_statement(statement, options);
138
+ auto request = core::impl::build_analytics_request(
139
+ std::move(statement), std::move(options), bucket_name_, name_, obs_rec->operation_span());
140
+ core::impl::dispatch_analytics_stream(
141
+ core_, std::move(request), std::move(obs_rec), std::move(handler));
142
+ }
143
+
114
144
  void search(std::string index_name,
115
145
  couchbase::search_request request,
116
146
  search_options::built options,
@@ -205,6 +235,26 @@ scope::query(std::string statement, const query_options& options) const
205
235
  return future;
206
236
  }
207
237
 
238
+ void
239
+ scope::query_stream(std::string statement,
240
+ const query_options& options,
241
+ query_stream_handler&& handler) const
242
+ {
243
+ return impl_->query_stream(std::move(statement), options.build(), std::move(handler));
244
+ }
245
+
246
+ auto
247
+ scope::query_stream(std::string statement, const query_options& options) const
248
+ -> std::future<std::pair<error, query_stream_result>>
249
+ {
250
+ auto barrier = std::make_shared<std::promise<std::pair<error, query_stream_result>>>();
251
+ auto future = barrier->get_future();
252
+ query_stream(std::move(statement), options, [barrier](auto err, auto result) {
253
+ barrier->set_value({ std::move(err), std::move(result) });
254
+ });
255
+ return future;
256
+ }
257
+
208
258
  void
209
259
  scope::analytics_query(std::string statement,
210
260
  const analytics_options& options,
@@ -225,6 +275,26 @@ scope::analytics_query(std::string statement, const analytics_options& options)
225
275
  return future;
226
276
  }
227
277
 
278
+ void
279
+ scope::analytics_query_stream(std::string statement,
280
+ const analytics_options& options,
281
+ analytics_stream_handler&& handler) const
282
+ {
283
+ return impl_->analytics_query_stream(std::move(statement), options.build(), std::move(handler));
284
+ }
285
+
286
+ auto
287
+ scope::analytics_query_stream(std::string statement, const analytics_options& options) const
288
+ -> std::future<std::pair<error, analytics_stream_result>>
289
+ {
290
+ auto barrier = std::make_shared<std::promise<std::pair<error, analytics_stream_result>>>();
291
+ auto future = barrier->get_future();
292
+ analytics_query_stream(std::move(statement), options, [barrier](auto err, auto result) {
293
+ barrier->set_value({ std::move(err), std::move(result) });
294
+ });
295
+ return future;
296
+ }
297
+
228
298
  void
229
299
  scope::search(std::string index_name,
230
300
  search_request request,
@@ -17,6 +17,7 @@
17
17
 
18
18
  #include "core/cluster.hxx"
19
19
  #include "core/operations/document_search.hxx"
20
+ #include "core/search_scoring.hxx"
20
21
  #include "core/utils/json.hxx"
21
22
  #include "encoded_search_facet.hxx"
22
23
  #include "encoded_search_query.hxx"
@@ -100,7 +101,7 @@ map_facets(const std::map<std::string, std::shared_ptr<search_facet>, std::less<
100
101
  }
101
102
 
102
103
  auto
103
- map_raw(std::map<std::string, codec::binary, std::less<>>& raw)
104
+ map_raw(const std::map<std::string, codec::binary, std::less<>>& raw)
104
105
  -> std::map<std::string, couchbase::core::json_string>
105
106
  {
106
107
  std::map<std::string, couchbase::core::json_string> core_raw{};
@@ -125,6 +126,33 @@ map_vector_query_combination(const std::optional<couchbase::vector_query_combina
125
126
  return {};
126
127
  }
127
128
 
129
+ auto
130
+ map_scoring(const search_scoring::built& scoring) -> core::search_scoring_mode
131
+ {
132
+ return std::visit(
133
+ [](const auto& mode) -> core::search_scoring_mode {
134
+ using T = std::decay_t<decltype(mode)>;
135
+ if constexpr (std::is_same_v<T, search_scoring::built::none>) {
136
+ return core::search_scoring_none{};
137
+ } else if constexpr (std::is_same_v<T, search_scoring::built::reciprocal_rank_fusion>) {
138
+ return core::search_scoring_reciprocal_rank_fusion{ mode.rank_constant, mode.window_size };
139
+ } else if constexpr (std::is_same_v<T, search_scoring::built::relative_score_fusion>) {
140
+ return core::search_scoring_relative_score_fusion{ mode.window_size };
141
+ }
142
+ },
143
+ scoring.mode);
144
+ }
145
+
146
+ void
147
+ apply_scoring(core::operations::search_request& request,
148
+ const std::optional<search_scoring::built>& scoring)
149
+ {
150
+ if (!scoring.has_value()) {
151
+ return;
152
+ }
153
+ request.scoring = map_scoring(scoring.value());
154
+ }
155
+
128
156
  } // namespace
129
157
 
130
158
  auto
@@ -168,6 +196,7 @@ build_search_request(std::string index_name,
168
196
  options.timeout,
169
197
  };
170
198
  request.parent_span = std::move(op_span);
199
+ apply_scoring(request, options.scoring);
171
200
  return request;
172
201
  }
173
202
 
@@ -213,6 +242,7 @@ build_search_request(std::string index_name,
213
242
  options.timeout,
214
243
  };
215
244
  core_request.parent_span = std::move(op_span);
245
+ apply_scoring(core_request, options.scoring);
216
246
 
217
247
  if (auto vector_search = request.vector_search(); vector_search.has_value()) {
218
248
  core_request.vector_search = core::utils::json::generate_binary(vector_search->query);
@@ -0,0 +1,130 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2026-Present Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include <couchbase/cluster.hxx>
19
+ #include <couchbase/transactions.hxx>
20
+
21
+ #include "core/impl/error.hxx"
22
+ #include "core/transactions.hxx"
23
+ #include "core/transactions/attempt_context_impl.hxx"
24
+ #include "core/transactions/exceptions.hxx"
25
+
26
+ #include <spdlog/fmt/bundled/format.h>
27
+
28
+ #include <couchbase/fmt/error.hxx>
29
+
30
+ namespace couchbase::transactions
31
+ {
32
+ void
33
+ maybe_throw(error err)
34
+ {
35
+ if (err && err.ec() != errc::transaction_op::transaction_op_failed) {
36
+ // We intentionally don't handle transaction_op_failed here, as we must have cached the
37
+ // transaction error internally already, which has the full context with the right error class
38
+ // etc.
39
+ if (err.ec().category() == core::impl::transaction_op_category()) {
40
+ throw core::transactions::op_exception(std::move(err));
41
+ }
42
+ throw std::system_error(err.ec(), fmt::format("{}", err));
43
+ }
44
+ }
45
+
46
+ class transactions_impl
47
+ {
48
+ public:
49
+ explicit transactions_impl(std::shared_ptr<core::transactions::transactions> core_txns)
50
+ : core_txns_{ std::move(core_txns) }
51
+ {
52
+ }
53
+
54
+ auto run(txn_logic&& logic, const transaction_options& options) const
55
+ -> std::pair<error, transaction_result>
56
+ {
57
+ try {
58
+ auto res = core_txns_->run(options, [logic = std::move(logic)](auto ctx) mutable {
59
+ const auto err =
60
+ logic(std::static_pointer_cast<core::transactions::attempt_context_impl>(ctx));
61
+ maybe_throw(std::move(err));
62
+ });
63
+ return { {}, res };
64
+ } catch (const core::transactions::transaction_exception& e) {
65
+ auto [err_ctx, res_from_exc] = e.get_transaction_result();
66
+ return std::make_pair(core::impl::make_error(err_ctx), std::move(res_from_exc));
67
+ }
68
+ }
69
+
70
+ void run(async_txn_logic&& logic,
71
+ async_txn_complete_logic&& callback,
72
+ const transaction_options& options) const
73
+ {
74
+ core_txns_->run(
75
+ options,
76
+ [logic = std::move(logic)](auto ctx) mutable {
77
+ auto err = logic(std::static_pointer_cast<core::transactions::attempt_context_impl>(ctx));
78
+ maybe_throw(std::move(err));
79
+ },
80
+ [callback = std::move(callback)](auto exc, auto res) mutable {
81
+ if (exc.has_value()) {
82
+ auto [err_ctx, res_from_exc] = exc.value().get_transaction_result();
83
+ return callback(core::impl::make_error(err_ctx), std::move(res_from_exc));
84
+ }
85
+ return callback({}, std::move(res.value()));
86
+ });
87
+ }
88
+
89
+ [[nodiscard]] auto core() const -> std::shared_ptr<core::transactions::transactions>
90
+ {
91
+ return core_txns_;
92
+ }
93
+
94
+ private:
95
+ std::shared_ptr<core::transactions::transactions> core_txns_;
96
+ };
97
+
98
+ transactions::transactions(std::shared_ptr<core::transactions::transactions> core_txns)
99
+ : impl_{ std::make_shared<transactions_impl>(std::move(core_txns)) }
100
+ {
101
+ }
102
+
103
+ auto
104
+ transactions::run(txn_logic&& logic, const transaction_options& cfg)
105
+ -> std::pair<error, transaction_result>
106
+ {
107
+ return impl_->run(std::move(logic), cfg);
108
+ }
109
+
110
+ void
111
+ transactions::run(async_txn_logic&& logic,
112
+ async_txn_complete_logic&& complete_callback,
113
+ const transaction_options& cfg)
114
+ {
115
+ return impl_->run(std::move(logic), std::move(complete_callback), cfg);
116
+ }
117
+ } // namespace couchbase::transactions
118
+
119
+ namespace couchbase::core::transactions
120
+ {
121
+ auto
122
+ get_core_transactions(const std::shared_ptr<couchbase::transactions::transactions>& transactions)
123
+ -> std::shared_ptr<core::transactions::transactions>
124
+ {
125
+ return reinterpret_cast<const std::shared_ptr<couchbase::transactions::transactions_impl>*>(
126
+ transactions.get())
127
+ ->get()
128
+ ->core();
129
+ }
130
+ } // namespace couchbase::core::transactions