couchbase 3.8.2 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (473) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/ext/CMakeLists.txt +16 -1
  4. data/ext/cache/cpm/{CPM_0.42.0.cmake → CPM_0.43.1.cmake} +18 -2
  5. data/ext/cache/extconf_include.rb +6 -6
  6. data/ext/cache/mozilla-ca-bundle.crt +121 -55
  7. data/ext/cache/mozilla-ca-bundle.sha256 +1 -1
  8. data/ext/couchbase/CMakeLists.txt +130 -21
  9. data/ext/couchbase/cmake/APKBUILD.in +25 -4
  10. data/ext/couchbase/cmake/Bundler.cmake +143 -34
  11. data/ext/couchbase/cmake/CPM.cmake +126 -12
  12. data/ext/couchbase/cmake/Cache.cmake +7 -1
  13. data/ext/couchbase/cmake/CompilerOptions.cmake +1 -1
  14. data/ext/couchbase/cmake/CompilerWarnings.cmake +17 -6
  15. data/ext/couchbase/cmake/GrpcProtobuf.cmake +481 -0
  16. data/ext/couchbase/cmake/OpenSSL.cmake +10 -3
  17. data/ext/couchbase/cmake/Packaging.cmake +519 -65
  18. data/ext/couchbase/cmake/Protostellar.cmake +250 -0
  19. data/ext/couchbase/cmake/Sanitizers.cmake +61 -50
  20. data/ext/couchbase/cmake/StaticAnalyzers.cmake +16 -5
  21. data/ext/couchbase/cmake/TestFramework.cmake +283 -0
  22. data/ext/couchbase/cmake/TestFrameworkAddTests.cmake +51 -0
  23. data/ext/couchbase/cmake/Testing.cmake +27 -9
  24. data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +141 -8
  25. data/ext/couchbase/cmake/VersionInfo.cmake +50 -0
  26. data/ext/couchbase/cmake/build_config.hxx.in +2 -1
  27. data/ext/couchbase/cmake/couchbase-cxx-client.spec.in +101 -23
  28. data/ext/couchbase/cmake/couchbase_cxx_client_static-config.cmake.in +7 -0
  29. data/ext/couchbase/cmake/couchbase_cxx_client_static.pc.in +2 -0
  30. data/ext/couchbase/cmake/gRPC_Generate.cmake +58 -0
  31. data/ext/couchbase/cmake/protobuf_Generate.cmake +69 -0
  32. data/ext/couchbase/cmake/require-git-checkout.cmake +25 -0
  33. data/ext/couchbase/cmake/tarball_glob.txt +49 -0
  34. data/ext/couchbase/core/agent_group.cxx +0 -8
  35. data/ext/couchbase/core/agent_group.hxx +0 -4
  36. data/ext/couchbase/core/analytics_stream.cxx +314 -0
  37. data/ext/couchbase/core/analytics_stream.hxx +129 -0
  38. data/ext/couchbase/core/analytics_stream_component.cxx +245 -0
  39. data/ext/couchbase/core/analytics_stream_component.hxx +67 -0
  40. data/ext/couchbase/core/app_telemetry_meter.cxx +25 -3
  41. data/ext/couchbase/core/app_telemetry_meter.hxx +77 -0
  42. data/ext/couchbase/core/app_telemetry_reporter.cxx +7 -6
  43. data/ext/couchbase/core/bucket.cxx +381 -87
  44. data/ext/couchbase/core/bucket.hxx +105 -25
  45. data/ext/couchbase/core/bucket_unit_test_api.hxx +57 -0
  46. data/ext/couchbase/core/capella.hxx +91 -0
  47. data/ext/couchbase/core/cluster.cxx +1554 -398
  48. data/ext/couchbase/core/cluster.hxx +39 -8
  49. data/ext/couchbase/core/cluster_agent.cxx +0 -8
  50. data/ext/couchbase/core/cluster_agent.hxx +0 -4
  51. data/ext/couchbase/core/cluster_options.hxx +26 -3
  52. data/ext/couchbase/core/collections_component.cxx +39 -15
  53. data/ext/couchbase/core/config_profiles.cxx +1 -1
  54. data/ext/couchbase/core/crud_component.cxx +2 -2
  55. data/ext/couchbase/core/crud_options.hxx +34 -0
  56. data/ext/couchbase/core/crypto/cbcrypto.cc +18 -13
  57. data/ext/couchbase/core/document_id_redaction.hxx +81 -0
  58. data/ext/couchbase/core/error_context/base_error_context.hxx +7 -6
  59. data/ext/couchbase/core/error_context/key_value.cxx +25 -2
  60. data/ext/couchbase/core/error_context/key_value.hxx +60 -33
  61. data/ext/couchbase/core/error_context/key_value_error_context.hxx +16 -15
  62. data/ext/couchbase/core/error_context/search.hxx +5 -0
  63. data/ext/couchbase/core/error_context/search_json.hxx +3 -0
  64. data/ext/couchbase/core/error_context/subdocument_error_context.hxx +19 -18
  65. data/ext/couchbase/core/error_context/view.hxx +5 -0
  66. data/ext/couchbase/core/free_form_http_request.cxx +237 -4
  67. data/ext/couchbase/core/free_form_http_request.hxx +41 -8
  68. data/ext/couchbase/core/http_component.cxx +300 -284
  69. data/ext/couchbase/core/http_component.hxx +0 -4
  70. data/ext/couchbase/core/impl/analytics.cxx +38 -5
  71. data/ext/couchbase/core/impl/analytics.hxx +34 -0
  72. data/ext/couchbase/core/impl/bootstrap_error.hxx +0 -3
  73. data/ext/couchbase/core/impl/collection.cxx +215 -202
  74. data/ext/couchbase/core/impl/collection_manager.cxx +1 -1
  75. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +45 -18
  76. data/ext/couchbase/core/impl/error.cxx +22 -2
  77. data/ext/couchbase/core/impl/internal_analytics_stream_result.hxx +111 -0
  78. data/ext/couchbase/core/impl/internal_query_stream_result.hxx +110 -0
  79. data/ext/couchbase/core/impl/invoke_with_node_id.hxx +15 -7
  80. data/ext/couchbase/core/impl/key_value_error_category.cxx +6 -0
  81. data/ext/couchbase/core/impl/lookup_in_replica.cxx +10 -10
  82. data/ext/couchbase/core/impl/lookup_in_replica.hxx +4 -4
  83. data/ext/couchbase/core/impl/node_id.cxx +49 -13
  84. data/ext/couchbase/core/impl/observability_recorder.cxx +12 -2
  85. data/ext/couchbase/core/impl/observe_poll.cxx +63 -23
  86. data/ext/couchbase/core/impl/public_analytics_stream_result.cxx +372 -0
  87. data/ext/couchbase/core/impl/public_bucket.cxx +48 -2
  88. data/ext/couchbase/core/impl/public_cluster.cxx +229 -42
  89. data/ext/couchbase/core/impl/public_query_stream_result.cxx +380 -0
  90. data/ext/couchbase/core/impl/public_transaction_get_multi_replicas_from_preferred_server_group_result.cxx +42 -0
  91. data/ext/couchbase/core/impl/public_transaction_get_multi_result.cxx +42 -0
  92. data/ext/couchbase/core/impl/query.cxx +76 -7
  93. data/ext/couchbase/core/impl/query.hxx +36 -0
  94. data/ext/couchbase/core/impl/query_index_manager.cxx +54 -1
  95. data/ext/couchbase/core/impl/replica_utils.cxx +101 -0
  96. data/ext/couchbase/core/impl/replica_utils.hxx +64 -0
  97. data/ext/couchbase/core/impl/resolve_node_id.hxx +125 -0
  98. data/ext/couchbase/core/impl/scope.cxx +70 -0
  99. data/ext/couchbase/core/impl/search.cxx +31 -1
  100. data/ext/couchbase/core/impl/transactions.cxx +130 -0
  101. data/ext/couchbase/core/impl/wait_until_ready.cxx +439 -0
  102. data/ext/couchbase/core/impl/wait_until_ready.hxx +110 -0
  103. data/ext/couchbase/core/impl/with_bucket_config_or_timeout.hxx +180 -0
  104. data/ext/couchbase/core/impl/with_cancellation.hxx +11 -3
  105. data/ext/couchbase/core/io/configuration_belongs_to_session.hxx +1 -2
  106. data/ext/couchbase/core/io/dns_client.cxx +122 -45
  107. data/ext/couchbase/core/io/dns_codec.hxx +21 -21
  108. data/ext/couchbase/core/io/dns_config.cxx +21 -6
  109. data/ext/couchbase/core/io/http_command.hxx +149 -121
  110. data/ext/couchbase/core/io/http_error.hxx +107 -0
  111. data/ext/couchbase/core/io/http_parser.cxx +4 -0
  112. data/ext/couchbase/core/io/http_session.cxx +606 -154
  113. data/ext/couchbase/core/io/http_session.hxx +147 -30
  114. data/ext/couchbase/core/io/http_session_manager.hxx +329 -382
  115. data/ext/couchbase/core/io/http_streaming_parser.cxx +4 -0
  116. data/ext/couchbase/core/io/http_streaming_response.cxx +328 -43
  117. data/ext/couchbase/core/io/http_streaming_response.hxx +37 -3
  118. data/ext/couchbase/core/io/mcbp_buffer_pool.hxx +122 -0
  119. data/ext/couchbase/core/io/mcbp_command.hxx +150 -51
  120. data/ext/couchbase/core/io/mcbp_command_id.hxx +45 -0
  121. data/ext/couchbase/core/io/mcbp_message.cxx +9 -1
  122. data/ext/couchbase/core/io/mcbp_message.hxx +19 -0
  123. data/ext/couchbase/core/io/mcbp_output_queue.hxx +150 -0
  124. data/ext/couchbase/core/io/mcbp_parser.cxx +24 -7
  125. data/ext/couchbase/core/io/mcbp_session.cxx +309 -301
  126. data/ext/couchbase/core/io/mcbp_session.hxx +27 -18
  127. data/ext/couchbase/core/io/opaque_ring_table.hxx +145 -0
  128. data/ext/couchbase/core/io/retry_orchestrator.hxx +6 -5
  129. data/ext/couchbase/core/io/stream_deadline.hxx +50 -0
  130. data/ext/couchbase/core/io/streams.cxx +151 -21
  131. data/ext/couchbase/core/io/streams.hxx +48 -1
  132. data/ext/couchbase/core/logger/logger.cxx +36 -0
  133. data/ext/couchbase/core/logger/logger.hxx +46 -6
  134. data/ext/couchbase/core/logger/redaction.hxx +503 -0
  135. data/ext/couchbase/core/management/eventing_status.hxx +10 -0
  136. data/ext/couchbase/core/management/eventing_status_json.hxx +4 -0
  137. data/ext/couchbase/core/mcbp/big_endian.cxx +1 -1
  138. data/ext/couchbase/core/mcbp/codec.cxx +27 -18
  139. data/ext/couchbase/core/mcbp/operation_queue.cxx +2 -2
  140. data/ext/couchbase/core/mcbp/packet.hxx +2 -2
  141. data/ext/couchbase/core/mcbp/queue_request.cxx +23 -6
  142. data/ext/couchbase/core/mcbp/queue_request.hxx +21 -0
  143. data/ext/couchbase/core/meta/features.hxx +13 -0
  144. data/ext/couchbase/core/meta/version.cxx +6 -3
  145. data/ext/couchbase/core/metrics/logging_meter.cxx +5 -10
  146. data/ext/couchbase/core/metrics/meter_wrapper.cxx +63 -33
  147. data/ext/couchbase/core/metrics/meter_wrapper.hxx +55 -3
  148. data/ext/couchbase/core/operations/analytics_response_parsing.cxx +177 -0
  149. data/ext/couchbase/core/operations/analytics_response_parsing.hxx +55 -0
  150. data/ext/couchbase/core/operations/document_analytics.cxx +48 -130
  151. data/ext/couchbase/core/operations/document_analytics.hxx +14 -2
  152. data/ext/couchbase/core/operations/document_get_all_replicas.hxx +15 -7
  153. data/ext/couchbase/core/operations/document_get_any_replica.hxx +19 -8
  154. data/ext/couchbase/core/operations/document_get_projected.cxx +1 -1
  155. data/ext/couchbase/core/{impl/get_replica.cxx → operations/document_get_replica.cxx} +21 -3
  156. data/ext/couchbase/core/{impl/get_replica.hxx → operations/document_get_replica.hxx} +31 -4
  157. data/ext/couchbase/core/operations/document_lookup_in.cxx +10 -10
  158. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +228 -198
  159. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +215 -217
  160. data/ext/couchbase/core/operations/document_query.cxx +108 -221
  161. data/ext/couchbase/core/operations/document_query.hxx +18 -4
  162. data/ext/couchbase/core/operations/document_search.cxx +52 -8
  163. data/ext/couchbase/core/operations/document_search.hxx +3 -0
  164. data/ext/couchbase/core/operations/document_view.cxx +3 -3
  165. data/ext/couchbase/core/operations/management/analytics_dataset_create.cxx +15 -8
  166. data/ext/couchbase/core/operations/management/analytics_dataset_create.hxx +4 -0
  167. data/ext/couchbase/core/operations/management/analytics_dataset_drop.cxx +11 -6
  168. data/ext/couchbase/core/operations/management/analytics_dataset_get_all.cxx +3 -2
  169. data/ext/couchbase/core/operations/management/analytics_dataverse_create.cxx +10 -5
  170. data/ext/couchbase/core/operations/management/analytics_dataverse_drop.cxx +10 -5
  171. data/ext/couchbase/core/operations/management/analytics_get_pending_mutations.cxx +2 -1
  172. data/ext/couchbase/core/operations/management/analytics_index_create.cxx +33 -8
  173. data/ext/couchbase/core/operations/management/analytics_index_drop.cxx +12 -7
  174. data/ext/couchbase/core/operations/management/analytics_index_get_all.cxx +3 -2
  175. data/ext/couchbase/core/operations/management/analytics_link_connect.cxx +11 -6
  176. data/ext/couchbase/core/operations/management/analytics_link_create.cxx +3 -3
  177. data/ext/couchbase/core/operations/management/analytics_link_disconnect.cxx +10 -4
  178. data/ext/couchbase/core/operations/management/analytics_link_drop.cxx +6 -4
  179. data/ext/couchbase/core/operations/management/analytics_link_get_all.cxx +4 -3
  180. data/ext/couchbase/core/operations/management/analytics_link_replace.cxx +3 -3
  181. data/ext/couchbase/core/operations/management/analytics_link_utils.hxx +1 -1
  182. data/ext/couchbase/core/operations/management/change_password.cxx +2 -0
  183. data/ext/couchbase/core/operations/management/collection_drop.cxx +5 -1
  184. data/ext/couchbase/core/operations/management/error_utils.cxx +59 -5
  185. data/ext/couchbase/core/operations/management/error_utils.hxx +29 -1
  186. data/ext/couchbase/core/operations/management/eventing_deploy_function.cxx +9 -3
  187. data/ext/couchbase/core/operations/management/eventing_drop_function.cxx +8 -3
  188. data/ext/couchbase/core/operations/management/eventing_get_all_functions.cxx +4 -2
  189. data/ext/couchbase/core/operations/management/eventing_get_function.cxx +5 -3
  190. data/ext/couchbase/core/operations/management/eventing_get_status.cxx +4 -2
  191. data/ext/couchbase/core/operations/management/eventing_pause_function.cxx +9 -3
  192. data/ext/couchbase/core/operations/management/eventing_resume_function.cxx +9 -3
  193. data/ext/couchbase/core/operations/management/eventing_undeploy_function.cxx +9 -3
  194. data/ext/couchbase/core/operations/management/eventing_upsert_function.cxx +9 -3
  195. data/ext/couchbase/core/operations/management/group_drop.cxx +3 -1
  196. data/ext/couchbase/core/operations/management/group_get.cxx +3 -1
  197. data/ext/couchbase/core/operations/management/group_upsert.cxx +11 -8
  198. data/ext/couchbase/core/operations/management/query_index_build.cxx +5 -2
  199. data/ext/couchbase/core/operations/management/query_index_create.cxx +9 -3
  200. data/ext/couchbase/core/operations/management/query_index_drop.cxx +11 -4
  201. data/ext/couchbase/core/operations/management/query_index_get_all.cxx +1 -1
  202. data/ext/couchbase/core/operations/management/query_index_get_all_deferred.cxx +1 -1
  203. data/ext/couchbase/core/operations/management/scope_drop.cxx +5 -1
  204. data/ext/couchbase/core/operations/management/scope_get_all.cxx +6 -1
  205. data/ext/couchbase/core/operations/management/search_get_stats.cxx +10 -0
  206. data/ext/couchbase/core/operations/management/search_index_analyze_document.cxx +3 -2
  207. data/ext/couchbase/core/operations/management/search_index_control_ingest.cxx +4 -3
  208. data/ext/couchbase/core/operations/management/search_index_control_plan_freeze.cxx +4 -3
  209. data/ext/couchbase/core/operations/management/search_index_control_query.cxx +4 -3
  210. data/ext/couchbase/core/operations/management/search_index_drop.cxx +2 -2
  211. data/ext/couchbase/core/operations/management/search_index_get.cxx +2 -2
  212. data/ext/couchbase/core/operations/management/search_index_get_documents_count.cxx +3 -2
  213. data/ext/couchbase/core/operations/management/search_index_get_stats.cxx +3 -1
  214. data/ext/couchbase/core/operations/management/search_index_upsert.cxx +2 -2
  215. data/ext/couchbase/core/operations/management/user_drop.cxx +3 -1
  216. data/ext/couchbase/core/operations/management/user_get.cxx +3 -1
  217. data/ext/couchbase/core/operations/management/user_upsert.cxx +12 -9
  218. data/ext/couchbase/core/operations/management/view_index_drop.cxx +1 -1
  219. data/ext/couchbase/core/operations/management/view_index_get.cxx +1 -1
  220. data/ext/couchbase/core/operations/management/view_index_upsert.cxx +1 -1
  221. data/ext/couchbase/core/operations/operation_traits.hxx +18 -0
  222. data/ext/couchbase/core/operations/query_response_parsing.cxx +211 -0
  223. data/ext/couchbase/core/operations/query_response_parsing.hxx +60 -0
  224. data/ext/couchbase/core/operations_fwd.hxx +3 -3
  225. data/ext/couchbase/core/origin.cxx +54 -26
  226. data/ext/couchbase/core/origin.hxx +12 -0
  227. data/ext/couchbase/core/orphan_reporter.cxx +2 -1
  228. data/ext/couchbase/core/platform/random.cc +7 -7
  229. data/ext/couchbase/core/protocol/client_request.cxx +2 -2
  230. data/ext/couchbase/core/protocol/client_request.hxx +38 -21
  231. data/ext/couchbase/core/protocol/client_response.cxx +11 -3
  232. data/ext/couchbase/core/protocol/client_response.hxx +27 -6
  233. data/ext/couchbase/core/protocol/cmd_append.cxx +2 -2
  234. data/ext/couchbase/core/protocol/cmd_cluster_map_change_notification.cxx +2 -2
  235. data/ext/couchbase/core/protocol/cmd_decrement.cxx +6 -6
  236. data/ext/couchbase/core/protocol/cmd_get.cxx +1 -1
  237. data/ext/couchbase/core/protocol/cmd_get_and_lock.cxx +2 -2
  238. data/ext/couchbase/core/protocol/cmd_get_and_touch.cxx +2 -2
  239. data/ext/couchbase/core/protocol/cmd_get_cluster_config.cxx +6 -4
  240. data/ext/couchbase/core/protocol/cmd_get_collection_id.cxx +2 -2
  241. data/ext/couchbase/core/protocol/cmd_get_error_map.cxx +1 -1
  242. data/ext/couchbase/core/protocol/cmd_get_meta.cxx +4 -4
  243. data/ext/couchbase/core/protocol/cmd_get_replica.cxx +1 -1
  244. data/ext/couchbase/core/protocol/cmd_hello.cxx +1 -1
  245. data/ext/couchbase/core/protocol/cmd_increment.cxx +6 -6
  246. data/ext/couchbase/core/protocol/cmd_insert.cxx +4 -4
  247. data/ext/couchbase/core/protocol/cmd_lookup_in.cxx +4 -3
  248. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +4 -3
  249. data/ext/couchbase/core/protocol/cmd_mutate_in.cxx +10 -9
  250. data/ext/couchbase/core/protocol/cmd_mutate_in.hxx +10 -10
  251. data/ext/couchbase/core/protocol/cmd_observe_seqno.cxx +7 -7
  252. data/ext/couchbase/core/protocol/cmd_prepend.cxx +2 -2
  253. data/ext/couchbase/core/protocol/cmd_remove.cxx +2 -2
  254. data/ext/couchbase/core/protocol/cmd_replace.cxx +4 -4
  255. data/ext/couchbase/core/protocol/cmd_touch.cxx +1 -1
  256. data/ext/couchbase/core/protocol/cmd_upsert.cxx +4 -4
  257. data/ext/couchbase/core/protocol/datatype.hxx +118 -1
  258. data/ext/couchbase/core/protocol/frame_info_utils.cxx +1 -1
  259. data/ext/couchbase/core/protocol/magic.hxx +4 -0
  260. data/ext/couchbase/core/protocol/magic_fmt.hxx +3 -0
  261. data/ext/couchbase/core/protocol/server_request.hxx +3 -1
  262. data/ext/couchbase/core/protocol/status.cxx +1 -1
  263. data/ext/couchbase/core/protostellar/analytics_converter.hxx +171 -0
  264. data/ext/couchbase/core/protostellar/bucket_admin_converter.hxx +301 -0
  265. data/ext/couchbase/core/protostellar/collection_admin_converter.hxx +92 -0
  266. data/ext/couchbase/core/protostellar/component.cxx +2445 -0
  267. data/ext/couchbase/core/protostellar/component.hxx +391 -0
  268. data/ext/couchbase/core/{columnar/bootstrap_notification_subscriber.hxx → protostellar/compression_settings.hxx} +11 -10
  269. data/ext/couchbase/core/protostellar/credentials.cxx +233 -0
  270. data/ext/couchbase/core/protostellar/credentials.hxx +88 -0
  271. data/ext/couchbase/core/protostellar/dispatcher.cxx +57 -0
  272. data/ext/couchbase/core/protostellar/dispatcher.hxx +482 -0
  273. data/ext/couchbase/core/protostellar/error_utils.cxx +293 -0
  274. data/ext/couchbase/core/protostellar/error_utils.hxx +97 -0
  275. data/ext/couchbase/core/protostellar/json_payload.hxx +49 -0
  276. data/ext/couchbase/core/protostellar/kv_converter.cxx +70 -0
  277. data/ext/couchbase/core/protostellar/kv_converter.hxx +600 -0
  278. data/ext/couchbase/core/protostellar/query_converter.hxx +265 -0
  279. data/ext/couchbase/core/protostellar/query_index_admin_converter.hxx +291 -0
  280. data/ext/couchbase/core/protostellar/query_proto.hxx +73 -0
  281. data/ext/couchbase/core/protostellar/search_converter.hxx +244 -0
  282. data/ext/couchbase/core/protostellar/search_index_admin_converter.hxx +156 -0
  283. data/ext/couchbase/core/protostellar/view_converter.hxx +158 -0
  284. data/ext/couchbase/core/query_stream.cxx +447 -0
  285. data/ext/couchbase/core/query_stream.hxx +141 -0
  286. data/ext/couchbase/core/query_stream_component.cxx +243 -0
  287. data/ext/couchbase/core/query_stream_component.hxx +68 -0
  288. data/ext/couchbase/core/range_scan_load_balancer.cxx +15 -8
  289. data/ext/couchbase/core/range_scan_orchestrator.cxx +15 -3
  290. data/ext/couchbase/core/response_handler.hxx +41 -4
  291. data/ext/couchbase/core/row_streamer.cxx +332 -46
  292. data/ext/couchbase/core/row_streamer.hxx +31 -1
  293. data/ext/couchbase/core/row_streamer_options.hxx +55 -0
  294. data/ext/couchbase/core/sasl/client.h +3 -0
  295. data/ext/couchbase/core/search_scoring.hxx +66 -0
  296. data/ext/couchbase/core/stream_error_details.hxx +69 -0
  297. data/ext/couchbase/core/timeout_defaults.hxx +0 -1
  298. data/ext/couchbase/core/topology/capabilities.hxx +6 -0
  299. data/ext/couchbase/core/topology/capabilities_fmt.hxx +3 -0
  300. data/ext/couchbase/core/topology/configuration.cxx +21 -6
  301. data/ext/couchbase/core/topology/configuration.hxx +29 -8
  302. data/ext/couchbase/core/topology/configuration_json.hxx +3 -0
  303. data/ext/couchbase/core/topology/error_map_json.hxx +3 -1
  304. data/ext/couchbase/core/tracing/threshold_logging_tracer.cxx +70 -4
  305. data/ext/couchbase/core/tracing/threshold_logging_tracer.hxx +10 -0
  306. data/ext/couchbase/core/transactions/active_transaction_record.cxx +0 -34
  307. data/ext/couchbase/core/transactions/attempt_context_impl.cxx +108 -63
  308. data/ext/couchbase/core/transactions/attempt_context_impl.hxx +19 -2
  309. data/ext/couchbase/core/transactions/error_list.hxx +2 -2
  310. data/ext/couchbase/core/transactions/get_multi_fetch.hxx +160 -0
  311. data/ext/couchbase/core/transactions/get_multi_orchestrator.cxx +326 -125
  312. data/ext/couchbase/core/transactions/get_multi_transaction_id.hxx +62 -0
  313. data/ext/couchbase/core/transactions/internal/atr_entry.hxx +22 -2
  314. data/ext/couchbase/core/transactions/internal/transaction_context.hxx +2 -2
  315. data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +23 -1
  316. data/ext/couchbase/core/transactions/internal/utils.hxx +50 -14
  317. data/ext/couchbase/core/transactions/staged_mutation.cxx +23 -18
  318. data/ext/couchbase/core/transactions/staged_mutation.hxx +15 -4
  319. data/ext/couchbase/core/transactions/transaction_context.cxx +55 -40
  320. data/ext/couchbase/core/transactions/transaction_get_result.hxx +16 -0
  321. data/ext/couchbase/core/transactions/transactions.cxx +3 -55
  322. data/ext/couchbase/core/transactions/transactions_cleanup.cxx +165 -72
  323. data/ext/couchbase/core/transactions/transactions_config.cxx +0 -11
  324. data/ext/couchbase/core/transactions/utils.cxx +10 -0
  325. data/ext/couchbase/core/transactions/waitable_op_list.hxx +2 -2
  326. data/ext/couchbase/core/transactions.hxx +9 -9
  327. data/ext/couchbase/core/utils/byteswap.hxx +166 -9
  328. data/ext/couchbase/core/utils/connection_string.cxx +50 -85
  329. data/ext/couchbase/core/utils/connection_string.hxx +15 -0
  330. data/ext/couchbase/core/{columnar/backoff_calculator.hxx → utils/deadline.hxx} +13 -13
  331. data/ext/couchbase/core/utils/duration_parser.cxx +1 -1
  332. data/ext/couchbase/core/utils/json_streaming_lexer.cxx +48 -5
  333. data/ext/couchbase/core/utils/json_streaming_lexer.hxx +4 -1
  334. data/ext/couchbase/core/utils/movable_function.hxx +301 -52
  335. data/ext/couchbase/core/utils/name_codec.hxx +154 -11
  336. data/ext/couchbase/core/{columnar/retry_info.hxx → utils/process_id.hxx} +18 -13
  337. data/ext/couchbase/core/utils/url_codec.cxx +12 -51
  338. data/ext/couchbase/core/websocket_codec.cxx +3 -29
  339. data/ext/couchbase/couchbase/analytics_options.hxx +21 -10
  340. data/ext/couchbase/couchbase/analytics_row.hxx +105 -0
  341. data/ext/couchbase/couchbase/analytics_stream_result.hxx +266 -0
  342. data/ext/couchbase/couchbase/boolean_query.hxx +3 -3
  343. data/ext/couchbase/couchbase/bucket.hxx +57 -0
  344. data/ext/couchbase/couchbase/cluster.hxx +183 -5
  345. data/ext/couchbase/{core/columnar/security_options.hxx → couchbase/cluster_state.hxx} +18 -14
  346. data/ext/couchbase/couchbase/codec/lenient_json_transcoder.hxx +69 -0
  347. data/ext/couchbase/couchbase/collection.hxx +234 -14
  348. data/ext/couchbase/couchbase/common_durability_options.hxx +18 -3
  349. data/ext/couchbase/couchbase/conjunction_query.hxx +1 -1
  350. data/ext/couchbase/couchbase/create_dataset_analytics_options.hxx +6 -1
  351. data/ext/couchbase/couchbase/date_range_query.hxx +2 -2
  352. data/ext/couchbase/couchbase/decrement_options.hxx +1 -1
  353. data/ext/couchbase/couchbase/disjunction_query.hxx +1 -1
  354. data/ext/couchbase/couchbase/error.hxx +32 -0
  355. data/ext/couchbase/couchbase/error_codes.hxx +32 -0
  356. data/ext/couchbase/couchbase/geo_bounding_box_query.hxx +1 -1
  357. data/ext/couchbase/couchbase/geo_distance_query.hxx +1 -1
  358. data/ext/couchbase/couchbase/geo_polygon_query.hxx +1 -1
  359. data/ext/couchbase/couchbase/get_replica_options.hxx +68 -0
  360. data/ext/couchbase/couchbase/get_replica_result.hxx +5 -3
  361. data/ext/couchbase/couchbase/get_replica_strategy.hxx +161 -0
  362. data/ext/couchbase/couchbase/get_result.hxx +1 -0
  363. data/ext/couchbase/couchbase/increment_options.hxx +1 -1
  364. data/ext/couchbase/couchbase/insert_options.hxx +1 -1
  365. data/ext/couchbase/couchbase/match_phrase_query.hxx +1 -1
  366. data/ext/couchbase/couchbase/match_query.hxx +1 -1
  367. data/ext/couchbase/couchbase/mutate_in_options.hxx +3 -3
  368. data/ext/couchbase/couchbase/network_options.hxx +1 -1
  369. data/ext/couchbase/couchbase/node_id.hxx +42 -14
  370. data/ext/couchbase/couchbase/node_id_for_options.hxx +7 -1
  371. data/ext/couchbase/couchbase/node_ids_options.hxx +8 -2
  372. data/ext/couchbase/couchbase/numeric_range_query.hxx +1 -1
  373. data/ext/couchbase/couchbase/phrase_query.hxx +1 -1
  374. data/ext/couchbase/couchbase/prefix_query.hxx +1 -1
  375. data/ext/couchbase/couchbase/query_options.hxx +23 -12
  376. data/ext/couchbase/couchbase/query_row.hxx +111 -0
  377. data/ext/couchbase/couchbase/query_stream_result.hxx +302 -0
  378. data/ext/couchbase/couchbase/query_string_query.hxx +3 -3
  379. data/ext/couchbase/couchbase/regexp_query.hxx +1 -1
  380. data/ext/couchbase/couchbase/replace_options.hxx +3 -3
  381. data/ext/couchbase/couchbase/result.hxx +14 -3
  382. data/ext/couchbase/couchbase/scan_result_item.hxx +1 -0
  383. data/ext/couchbase/couchbase/scope.hxx +85 -2
  384. data/ext/couchbase/couchbase/search_options.hxx +58 -8
  385. data/ext/couchbase/couchbase/search_scoring.hxx +94 -0
  386. data/ext/couchbase/couchbase/search_scoring_none.hxx +51 -0
  387. data/ext/couchbase/couchbase/search_scoring_reciprocal_rank_fusion.hxx +88 -0
  388. data/ext/couchbase/couchbase/search_scoring_relative_score_fusion.hxx +70 -0
  389. data/ext/couchbase/couchbase/term_range_query.hxx +1 -1
  390. data/ext/couchbase/couchbase/tracing/request_span.hxx +46 -0
  391. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +23 -21
  392. data/ext/couchbase/couchbase/transactions/transaction_get_multi_result.hxx +24 -19
  393. data/ext/couchbase/couchbase/transactions/transaction_get_result.hxx +17 -3
  394. data/ext/couchbase/couchbase/transactions/transactions_config.hxx +4 -2
  395. data/ext/couchbase/couchbase/transactions.hxx +27 -18
  396. data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
  397. data/ext/couchbase/couchbase/vector_query.hxx +1 -1
  398. data/ext/couchbase/couchbase/wait_until_ready_options.hxx +111 -0
  399. data/ext/couchbase/couchbase/wildcard_query.hxx +1 -1
  400. data/ext/extconf.rb +15 -10
  401. data/ext/rcb_analytics.cxx +82 -102
  402. data/ext/rcb_backend.cxx +42 -36
  403. data/ext/rcb_backend.hxx +2 -0
  404. data/ext/rcb_buckets.cxx +47 -59
  405. data/ext/rcb_collections.cxx +42 -52
  406. data/ext/rcb_crud.cxx +345 -224
  407. data/ext/rcb_diagnostics.cxx +117 -27
  408. data/ext/rcb_exceptions.cxx +152 -99
  409. data/ext/rcb_exceptions.hxx +25 -0
  410. data/ext/rcb_extras.cxx +18 -19
  411. data/ext/rcb_hdr_histogram.cxx +19 -4
  412. data/ext/rcb_logger.cxx +202 -27
  413. data/ext/rcb_logger.hxx +12 -0
  414. data/ext/rcb_multi.cxx +40 -40
  415. data/ext/rcb_observability.cxx +7 -5
  416. data/ext/rcb_query.cxx +99 -142
  417. data/ext/rcb_range_scan.cxx +41 -35
  418. data/ext/rcb_search.cxx +98 -99
  419. data/ext/rcb_users.cxx +36 -56
  420. data/ext/rcb_utils.cxx +100 -37
  421. data/ext/rcb_utils.hxx +287 -33
  422. data/ext/rcb_version.cxx +1 -1
  423. data/ext/rcb_views.cxx +55 -51
  424. data/lib/active_support/cache/couchbase_store.rb +77 -12
  425. data/lib/couchbase/bucket.rb +20 -0
  426. data/lib/couchbase/cluster.rb +17 -0
  427. data/lib/couchbase/collection.rb +36 -1
  428. data/lib/couchbase/errors.rb +18 -0
  429. data/lib/couchbase/fork_hooks.rb +16 -7
  430. data/lib/couchbase/get_replica_strategy.rb +89 -0
  431. data/lib/couchbase/logger.rb +1 -1
  432. data/lib/couchbase/management/analytics_index_manager.rb +15 -15
  433. data/lib/couchbase/management/bucket_manager.rb +6 -6
  434. data/lib/couchbase/management/collection_manager.rb +6 -6
  435. data/lib/couchbase/management/query_index_manager.rb +7 -7
  436. data/lib/couchbase/management/search_index_manager.rb +13 -13
  437. data/lib/couchbase/management/user_manager.rb +10 -10
  438. data/lib/couchbase/management/view_index_manager.rb +5 -5
  439. data/lib/couchbase/options.rb +174 -42
  440. data/lib/couchbase/protostellar/bucket.rb +4 -0
  441. data/lib/couchbase/protostellar/cluster.rb +4 -0
  442. data/lib/couchbase/protostellar/collection.rb +4 -0
  443. data/lib/couchbase/protostellar/error_handling.rb +2 -1
  444. data/lib/couchbase/protostellar/request.rb +1 -0
  445. data/lib/couchbase/protostellar/request_generator/kv.rb +16 -2
  446. data/lib/couchbase/protostellar/request_generator/search.rb +17 -1
  447. data/lib/couchbase/protostellar/retry/orchestrator.rb +3 -2
  448. data/lib/couchbase/protostellar/retry/strategies/fail_fast.rb +33 -0
  449. data/lib/couchbase/protostellar/retry/strategies.rb +1 -0
  450. data/lib/couchbase/search_options.rb +95 -6
  451. data/lib/couchbase/version.rb +1 -1
  452. metadata +91 -33
  453. data/ext/0001-fix-build-for-mingw-w64-ucrt-x86_64-toolchain.patch +0 -40
  454. data/ext/couchbase/core/columnar/agent.hxx +0 -71
  455. data/ext/couchbase/core/columnar/agent_config.hxx +0 -59
  456. data/ext/couchbase/core/columnar/background_bootstrap_listener.hxx +0 -42
  457. data/ext/couchbase/core/columnar/backoff_calculator.cxx +0 -62
  458. data/ext/couchbase/core/columnar/columnar_agent.cxx +0 -147
  459. data/ext/couchbase/core/columnar/columnar_agent_config.cxx +0 -48
  460. data/ext/couchbase/core/columnar/columnar_error.cxx +0 -51
  461. data/ext/couchbase/core/columnar/database_management_options.hxx +0 -56
  462. data/ext/couchbase/core/columnar/error.hxx +0 -51
  463. data/ext/couchbase/core/columnar/error_codes.cxx +0 -107
  464. data/ext/couchbase/core/columnar/error_codes.hxx +0 -75
  465. data/ext/couchbase/core/columnar/management_component.cxx +0 -300
  466. data/ext/couchbase/core/columnar/management_component.hxx +0 -56
  467. data/ext/couchbase/core/columnar/query_component.cxx +0 -523
  468. data/ext/couchbase/core/columnar/query_component.hxx +0 -57
  469. data/ext/couchbase/core/columnar/query_options.hxx +0 -59
  470. data/ext/couchbase/core/columnar/query_result.cxx +0 -130
  471. data/ext/couchbase/core/columnar/query_result.hxx +0 -78
  472. data/ext/couchbase/core/io/config_tracker.cxx +0 -687
  473. data/ext/couchbase/core/io/config_tracker.hxx +0 -123
@@ -65,7 +65,7 @@ map_collection(std::string scope_name,
65
65
  }
66
66
 
67
67
  auto
68
- map_scope_specs(core::topology::collections_manifest& manifest)
68
+ map_scope_specs(const core::topology::collections_manifest& manifest)
69
69
  -> std::vector<couchbase::management::bucket::scope_spec>
70
70
  {
71
71
  std::vector<couchbase::management::bucket::scope_spec> scope_specs{};
@@ -21,9 +21,9 @@
21
21
  #include "core/io/dns_client.hxx"
22
22
  #include "core/io/dns_config.hxx"
23
23
  #include "core/logger/logger.hxx"
24
+ #include "core/logger/redaction.hxx"
24
25
  #include "core/origin.hxx"
25
26
  #include "core/topology/configuration.hxx"
26
- #include "core/utils/join_strings.hxx"
27
27
  #include "core/utils/movable_function.hxx"
28
28
 
29
29
  #include <couchbase/error_codes.hxx>
@@ -44,6 +44,27 @@
44
44
 
45
45
  namespace couchbase::core::impl
46
46
  {
47
+ namespace
48
+ {
49
+ auto
50
+ nameserver_address(const io::dns::dns_config& config) -> std::string
51
+ {
52
+ return fmt::format(
53
+ "{}:{}", config.nameserver().empty() ? "(system)" : config.nameserver(), config.port());
54
+ }
55
+
56
+ auto
57
+ node_addresses(const origin::node_list& nodes) -> std::vector<std::string>
58
+ {
59
+ std::vector<std::string> addresses;
60
+ addresses.reserve(nodes.size());
61
+ for (const auto& [host, port] : nodes) {
62
+ addresses.emplace_back(fmt::format("{}:{}", host, port));
63
+ }
64
+ return addresses;
65
+ }
66
+ } // namespace
67
+
47
68
  dns_srv_tracker::dns_srv_tracker(asio::io_context& ctx,
48
69
  std::string address,
49
70
  io::dns::dns_config config,
@@ -61,26 +82,37 @@ void
61
82
  dns_srv_tracker::get_srv_nodes(
62
83
  utils::movable_function<void(origin::node_list, std::error_code)> callback)
63
84
  {
64
- CB_LOG_DEBUG("Query DNS-SRV: address=\"{}\", service=\"{}\", nameserver=\"{}:{}\"",
65
- address_,
85
+ CB_LOG_DEBUG("Query DNS-SRV: name=\"{}._tcp.{}\", nameserver=\"{}\"",
66
86
  service_,
67
- config_.nameserver(),
68
- config_.port());
87
+ logger::system_data(address_),
88
+ logger::system_data(nameserver_address(config_)));
69
89
  dns_client_.query_srv(address_,
70
90
  service_,
71
91
  config_,
72
92
  [self = shared_from_this(), callback = std::move(callback)](
73
- couchbase::core::io::dns::dns_srv_response&& resp) mutable {
93
+ couchbase::core::io::dns::dns_srv_response&& resp) mutable -> void {
74
94
  origin::node_list nodes;
75
95
  if (resp.ec) {
76
96
  CB_LOG_WARNING("failed to fetch DNS SRV records for \"{}\" ({}), "
77
97
  "assuming that cluster is listening this address",
78
- self->address_,
98
+ logger::system_data(self->address_),
79
99
  resp.ec.message());
80
100
  } else if (resp.targets.empty() && self->address_ != "localhost") {
81
- CB_LOG_DEBUG("DNS SRV query returned 0 records for \"{}\", assuming "
82
- "that cluster is listening this address",
83
- self->address_);
101
+ if (self->config_.nameserver().empty()) {
102
+ CB_LOG_DEBUG("DNS SRV query returned 0 records for \"{}\", assuming "
103
+ "that cluster is listening this address",
104
+ logger::system_data(self->address_));
105
+ } else {
106
+ CB_LOG_WARNING(
107
+ "DNS SRV query for \"{}._tcp.{}\" returned 0 records from "
108
+ "nameserver \"{}\". The DNS server may not have SRV records "
109
+ "for this hostname, or may not support SRV lookups. "
110
+ "Falling back to direct connection to \"{}\".",
111
+ self->service_,
112
+ logger::system_data(self->address_),
113
+ logger::system_data(nameserver_address(self->config_)),
114
+ logger::system_data(self->address_));
115
+ }
84
116
  } else {
85
117
  nodes.reserve(resp.targets.size());
86
118
  for (const auto& address : resp.targets) {
@@ -115,14 +147,9 @@ dns_srv_tracker::do_dns_refresh()
115
147
 
116
148
  if (!listeners.empty()) {
117
149
  auto config = topology::make_blank_configuration(nodes, self->use_tls_, true);
118
- std::vector<std::string> endpoints;
119
- endpoints.reserve(nodes.size());
120
- for (const auto& [host, port] : nodes) {
121
- endpoints.emplace_back(fmt::format("\"{}:{}\"", host, port));
122
- }
123
150
  CB_LOG_DEBUG("generated configuration from DNS-SRV response \"{}\": [{}]",
124
- self->address_,
125
- utils::join_strings(endpoints, ", "));
151
+ logger::system_data(self->address_),
152
+ logger::system_data_list(node_addresses(nodes), logger::list_entries::quoted));
126
153
  for (const auto& listener : listeners) {
127
154
  listener->update_config(config);
128
155
  }
@@ -152,7 +179,7 @@ dns_srv_tracker::report_bootstrap_error(const std::string& endpoint, std::error_
152
179
  CB_LOG_DEBUG(
153
180
  "all nodes failed to bootstrap, triggering DNS-SRV refresh, ec={}, last endpoint=\"{}\"",
154
181
  ec.message(),
155
- endpoint);
182
+ logger::system_data(endpoint));
156
183
  return asio::post(asio::bind_executor(ctx_, [self = shared_from_this()]() mutable {
157
184
  self->do_dns_refresh();
158
185
  }));
@@ -86,6 +86,19 @@ error::error(std::error_code ec,
86
86
  {
87
87
  }
88
88
 
89
+ error::error(std::error_code ec,
90
+ std::string message,
91
+ couchbase::error_context ctx,
92
+ couchbase::error cause,
93
+ couchbase::node_id node_id)
94
+ : ec_{ ec }
95
+ , message_{ std::move(message) }
96
+ , ctx_{ std::move(ctx) }
97
+ , cause_{ std::make_shared<error>(std::move(cause)) }
98
+ , node_id_{ std::move(node_id) }
99
+ {
100
+ }
101
+
89
102
  auto
90
103
  error::ec() const -> std::error_code
91
104
  {
@@ -120,7 +133,8 @@ error::node_id() const -> const couchbase::node_id&
120
133
  return node_id_;
121
134
  }
122
135
 
123
- error::operator bool() const
136
+ error::
137
+ operator bool() const
124
138
  {
125
139
  return ec_.value() != 0;
126
140
  }
@@ -222,11 +236,17 @@ make_error(const core::transactions::op_exception& exc) -> couchbase::error
222
236
  }
223
237
 
224
238
  if (cause.has_value()) {
239
+ // Propagate node_id from the inner cause to the outer error. Without
240
+ // this, a transaction-wrapped KV failure would have outer.node_id() ==
241
+ // {} while cause().value().node_id() carries the real attribution —
242
+ // surprising for callers reading error.node_id() at the top level.
243
+ auto node_id = cause->node_id();
225
244
  return couchbase::error{
226
245
  transaction_op_errc_from_external_exception(exc.cause()),
227
246
  exc.what(),
228
247
  {},
229
- cause.value(),
248
+ std::move(*cause),
249
+ std::move(node_id),
230
250
  };
231
251
  }
232
252
 
@@ -0,0 +1,111 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2026. 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 <couchbase/analytics_meta_data.hxx>
21
+ #include <couchbase/analytics_stream_result.hxx>
22
+ #include <couchbase/error_codes.hxx>
23
+
24
+ #include "core/analytics_stream.hxx"
25
+
26
+ #include "core/error_context/analytics.hxx"
27
+ #include <atomic>
28
+ #include <future>
29
+
30
+ #include <memory>
31
+ #include <mutex>
32
+ #include <optional>
33
+ #include <system_error>
34
+ #include <utility>
35
+ #include <vector>
36
+
37
+ namespace couchbase::core::impl
38
+ {
39
+ class observability_recorder;
40
+ } // namespace couchbase::core::impl
41
+
42
+ namespace couchbase
43
+ {
44
+ class internal_analytics_stream_result
45
+ : public std::enable_shared_from_this<internal_analytics_stream_result>
46
+ {
47
+ public:
48
+ internal_analytics_stream_result(core::analytics_stream stream,
49
+ std::unique_ptr<core::impl::observability_recorder> obs_rec,
50
+ core::error_context::analytics ctx);
51
+ // Non-copyable and non-movable: holds a std::mutex and is only ever owned via shared_ptr.
52
+ internal_analytics_stream_result(const internal_analytics_stream_result&) = delete;
53
+ internal_analytics_stream_result(internal_analytics_stream_result&&) = delete;
54
+ auto operator=(const internal_analytics_stream_result&)
55
+ -> internal_analytics_stream_result& = delete;
56
+ auto operator=(internal_analytics_stream_result&&) -> internal_analytics_stream_result& = delete;
57
+
58
+ ~internal_analytics_stream_result();
59
+
60
+ void next(analytics_row_handler&& handler);
61
+ void cancel();
62
+
63
+ /**
64
+ * Returns the analytics signature captured from the response metadata.
65
+ */
66
+ [[nodiscard]] auto signature() const -> std::optional<codec::binary>;
67
+
68
+ /**
69
+ * Returns a future that resolves to analytics metadata once the stream reaches its terminal
70
+ * state (all rows consumed or an error). If the terminal has already been reached, returns a
71
+ * ready future built from the stored value.
72
+ */
73
+ auto meta_data() -> std::future<std::pair<error, analytics_meta_data>>;
74
+
75
+ private:
76
+ /**
77
+ * Called when next_row reports the terminal condition (nullopt row).
78
+ * Resolves the meta_data promise from the core stream.
79
+ *
80
+ * @param ec error code from the terminal next_row call (falsy = clean end)
81
+ */
82
+ void resolve_meta_data(std::error_code ec);
83
+
84
+ core::analytics_stream stream_;
85
+
86
+ // The request's error context as of a successfully started stream (statement, parameters, HTTP
87
+ // status, endpoint). A terminal error is reported against a copy of this with the trailer's
88
+ // diagnostics stamped on, so a mid-stream failure is as diagnosable as one that failed to start.
89
+ core::error_context::analytics ctx_;
90
+
91
+ // Observability recorder for the whole streaming operation. Its operation span already parents
92
+ // the request; finish() is called exactly once from resolve_meta_data() (the single terminal
93
+ // hook, reached on drain/error/cancel/destruction), recording the operation latency metric and
94
+ // ending the span — the same instrumentation buffered analytics_query() emits via its recorder.
95
+ std::unique_ptr<core::impl::observability_recorder> obs_rec_;
96
+
97
+ // Guards the "only one outstanding next()" contract. Set while a pull is in flight and cleared
98
+ // when its handler runs; a concurrent/overlapping next() is rejected with invalid_argument
99
+ // rather than racing the single-consumer channel. Shared across handle copies (they alias one
100
+ // stream), so copies cannot smuggle in a second concurrent pull either.
101
+ std::atomic_bool pull_in_flight_{ false };
102
+
103
+ mutable std::mutex meta_mutex_{};
104
+ bool terminal_reached_{ false };
105
+ std::pair<error, analytics_meta_data> terminal_value_{};
106
+ // meta_data() may be called any number of times before the stream terminates; each call parks a
107
+ // promise here and they are all satisfied together at the terminal. (A single shared promise
108
+ // would throw future_already_retrieved on the second call.)
109
+ std::vector<std::promise<std::pair<error, analytics_meta_data>>> pending_meta_promises_{};
110
+ };
111
+ } // namespace couchbase
@@ -0,0 +1,110 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2026. 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 <couchbase/error_codes.hxx>
21
+ #include <couchbase/query_meta_data.hxx>
22
+ #include <couchbase/query_stream_result.hxx>
23
+
24
+ #include "core/query_stream.hxx"
25
+
26
+ #include "core/error_context/query.hxx"
27
+ #include <atomic>
28
+ #include <future>
29
+
30
+ #include <memory>
31
+ #include <mutex>
32
+ #include <optional>
33
+ #include <system_error>
34
+ #include <utility>
35
+ #include <vector>
36
+
37
+ namespace couchbase::core::impl
38
+ {
39
+ class observability_recorder;
40
+ } // namespace couchbase::core::impl
41
+
42
+ namespace couchbase
43
+ {
44
+ class internal_query_stream_result
45
+ : public std::enable_shared_from_this<internal_query_stream_result>
46
+ {
47
+ public:
48
+ internal_query_stream_result(core::query_stream stream,
49
+ std::unique_ptr<core::impl::observability_recorder> obs_rec,
50
+ core::error_context::query ctx);
51
+ // Non-copyable and non-movable: holds a std::mutex and is only ever owned via shared_ptr.
52
+ internal_query_stream_result(const internal_query_stream_result&) = delete;
53
+ internal_query_stream_result(internal_query_stream_result&&) = delete;
54
+ auto operator=(const internal_query_stream_result&) -> internal_query_stream_result& = delete;
55
+ auto operator=(internal_query_stream_result&&) -> internal_query_stream_result& = delete;
56
+
57
+ ~internal_query_stream_result();
58
+
59
+ void next(query_row_handler&& handler);
60
+ void cancel();
61
+
62
+ /**
63
+ * Returns the query signature captured from the response metadata.
64
+ */
65
+ [[nodiscard]] auto signature() const -> std::optional<codec::binary>;
66
+
67
+ /**
68
+ * Returns a future that resolves to query metadata once the stream reaches its terminal state
69
+ * (all rows consumed or an error). If the terminal has already been reached, returns a
70
+ * ready future built from the stored value.
71
+ */
72
+ auto meta_data() -> std::future<std::pair<error, query_meta_data>>;
73
+
74
+ private:
75
+ /**
76
+ * Called when next_row reports the terminal condition (nullopt row).
77
+ * Resolves the meta_data promise from the core stream.
78
+ *
79
+ * @param ec error code from the terminal next_row call (falsy = clean end)
80
+ */
81
+ void resolve_meta_data(std::error_code ec);
82
+
83
+ core::query_stream stream_;
84
+
85
+ // The request's error context as of a successfully started stream (statement, parameters, HTTP
86
+ // status, endpoint). A terminal error is reported against a copy of this with the trailer's
87
+ // diagnostics stamped on, so a mid-stream failure is as diagnosable as one that failed to start.
88
+ core::error_context::query ctx_;
89
+
90
+ // Observability recorder for the whole streaming operation. Its operation span already parents
91
+ // the request; finish() is called exactly once from resolve_meta_data() (the single terminal
92
+ // hook, reached on drain/error/cancel/destruction), recording the operation latency metric and
93
+ // ending the span — the same instrumentation the buffered query() emits via its recorder.
94
+ std::unique_ptr<core::impl::observability_recorder> obs_rec_;
95
+
96
+ // Guards the "only one outstanding next()" contract. Set while a pull is in flight and cleared
97
+ // when its handler runs; a concurrent/overlapping next() is rejected with invalid_argument
98
+ // rather than racing the single-consumer channel. Shared across handle copies (they alias one
99
+ // stream), so copies cannot smuggle in a second concurrent pull either.
100
+ std::atomic_bool pull_in_flight_{ false };
101
+
102
+ mutable std::mutex meta_mutex_{};
103
+ bool terminal_reached_{ false };
104
+ std::pair<error, query_meta_data> terminal_value_{};
105
+ // meta_data() may be called any number of times before the stream terminates; each call parks a
106
+ // promise here and they are all satisfied together at the terminal. (A single shared promise
107
+ // would throw future_already_retrieved on the second call.)
108
+ std::vector<std::promise<std::pair<error, query_meta_data>>> pending_meta_promises_{};
109
+ };
110
+ } // namespace couchbase
@@ -30,15 +30,23 @@ namespace couchbase::core::impl
30
30
  * allocation a type-erasing wrapper would introduce for the sole purpose of
31
31
  * setting node_id - that extra allocation was the source of the 18
32
32
  * "Potential memory leak" reports clang-static-analyzer flagged against the
33
- * previous wrap_with_node_id helper. The handler is taken by forwarding
34
- * reference and perfect-forwarded to the invocation so that callers moving
35
- * an rvalue handler consume it without an intermediate copy.
33
+ * previous wrap_with_node_id helper.
34
+ *
35
+ * Parameters are taken by forwarding reference and perfect-forwarded to the
36
+ * invocation. Callers at the SDK boundary always pass rvalues (the result
37
+ * and error are constructed in-place at the response decoder), so this
38
+ * pattern eliminates the intermediate parameter-slot move and the
39
+ * @c invoke_with_node_id helper's overhead actually approaches zero — what
40
+ * the commit message claimed but the original (by-value) signatures did
41
+ * not quite deliver.
36
42
  */
37
- template<typename Handler, typename Result>
43
+ template<typename Handler, typename Error, typename Result>
38
44
  void
39
- invoke_with_node_id(Handler&& handler, couchbase::error err, Result result)
45
+ invoke_with_node_id(Handler&& handler, Error&& err, Result&& result)
40
46
  {
41
- result.node_id(err.node_id());
42
- std::forward<Handler>(handler)(std::move(err), std::move(result));
47
+ // Read node_id() BEFORE we move err, otherwise the read would see a
48
+ // moved-from string and yield a default node_id.
49
+ result.set_node_id_internal(err.node_id());
50
+ std::forward<Handler>(handler)(std::forward<Error>(err), std::forward<Result>(result));
43
51
  }
44
52
  } // namespace couchbase::core::impl
@@ -90,6 +90,12 @@ struct key_value_error_category : std::error_category {
90
90
  return "mutation_token_outdated (133)";
91
91
  case errc::key_value::range_scan_completed:
92
92
  return "range_scan_completed (134)";
93
+ case errc::key_value::document_not_found_on_replica:
94
+ return "document_not_found_on_replica (135)";
95
+ case errc::key_value::replica_index_out_of_bounds:
96
+ return "replica_index_out_of_bounds (136)";
97
+ case errc::key_value::replica_index_currently_unavailable:
98
+ return "replica_index_currently_unavailable (137)";
93
99
  }
94
100
  return "FIXME: unknown error code (recompile with newer library): couchbase.key_value." +
95
101
  std::to_string(ev);
@@ -59,7 +59,6 @@ lookup_in_replica_request::make_response(key_value_error_context&& ctx,
59
59
  bool deleted = false;
60
60
  couchbase::cas cas{};
61
61
  std::vector<lookup_in_replica_response::entry> fields{};
62
- std::error_code ec = ctx.ec();
63
62
  std::optional<std::size_t> first_error_index{};
64
63
  std::optional<std::string> first_error_path{};
65
64
 
@@ -83,13 +82,14 @@ lookup_in_replica_request::make_response(key_value_error_context&& ctx,
83
82
  static_cast<std::uint16_t>(res_entry.status));
84
83
  if (fields[i].opcode == protocol::subdoc_opcode::exists &&
85
84
  fields[i].ec == errc::key_value::path_not_found) {
85
+ // exists treats path_not_found as success ("false"); suppress the error so the
86
+ // exists flag below carries the result instead.
86
87
  fields[i].ec.clear();
87
88
  }
88
- if (!fields[i].ec && !ctx.ec()) {
89
- ec = fields[i].ec;
90
- }
91
- if (!first_error_index && !fields[i].ec) {
92
- first_error_index = i;
89
+ // Use original_index, not wire-order i: XATTR specs are reordered during encode_to(),
90
+ // so i != original_index (was the CXXCBC-788 bug).
91
+ if (!first_error_index && fields[i].ec) {
92
+ first_error_index = fields[i].original_index;
93
93
  first_error_path = fields[i].path;
94
94
  }
95
95
  fields[i].exists = res_entry.status == key_value_status_code::success ||
@@ -100,16 +100,16 @@ lookup_in_replica_request::make_response(key_value_error_context&& ctx,
100
100
  fields[i].value = utils::to_binary(res_entry.value);
101
101
  }
102
102
  }
103
- if (!ec) {
104
- cas = encoded.cas();
105
- }
103
+ // Always assign CAS; previously guarded by a local ec that was polluted by per-path
104
+ // errors, zeroing CAS whenever any path failed (CXXCBC-788).
105
+ cas = encoded.cas();
106
106
  std::sort(fields.begin(), fields.end(), [](const auto& lhs, const auto& rhs) {
107
107
  return lhs.original_index < rhs.original_index;
108
108
  });
109
109
  }
110
110
 
111
111
  return lookup_in_replica_response{
112
- make_subdocument_error_context(ctx, ec, first_error_path, first_error_index, deleted),
112
+ make_subdocument_error_context(ctx, ctx.ec(), first_error_path, first_error_index, deleted),
113
113
  cas,
114
114
  std::move(fields),
115
115
  deleted,
@@ -35,10 +35,10 @@ struct lookup_in_replica_response {
35
35
  struct entry {
36
36
  std::string path;
37
37
  couchbase::codec::binary value;
38
- std::size_t original_index;
39
- bool exists;
40
- protocol::subdoc_opcode opcode;
41
- key_value_status_code status;
38
+ std::size_t original_index{};
39
+ bool exists{};
40
+ protocol::subdoc_opcode opcode{};
41
+ key_value_status_code status{};
42
42
  std::error_code ec{};
43
43
  };
44
44
  subdocument_error_context ctx{};
@@ -1,6 +1,6 @@
1
1
  /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
2
  /*
3
- * Copyright 2024-Present Couchbase, Inc.
3
+ * Copyright 2026-Present Couchbase, Inc.
4
4
  *
5
5
  * Licensed under the Apache License, Version 2.0 (the "License");
6
6
  * you may not use this file except in compliance with the License.
@@ -19,10 +19,10 @@
19
19
 
20
20
  #include "core/utils/crc32.hxx"
21
21
 
22
- #include <spdlog/fmt/bundled/core.h>
23
-
22
+ #include <array>
24
23
  #include <cstdint>
25
24
  #include <string>
25
+ #include <utility>
26
26
 
27
27
  namespace couchbase
28
28
  {
@@ -39,9 +39,19 @@ namespace
39
39
  auto
40
40
  derive_fallback_id(const std::string& hostname, std::uint16_t port) -> std::string
41
41
  {
42
- auto input = fmt::format("{}:{}", hostname, port);
42
+ auto input = hostname + ':' + std::to_string(port);
43
43
  auto crc = core::utils::hash_crc32(input.data(), input.size());
44
- return fmt::format("{:08x}", crc);
44
+
45
+ // Format as 8 lowercase hex chars, zero-padded — avoids a heavy fmt include
46
+ // for what amounts to a two-line hex conversion.
47
+ static constexpr std::array<char, 16> hex_digits{ '0', '1', '2', '3', '4', '5', '6', '7',
48
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
49
+ std::string out;
50
+ out.reserve(8);
51
+ for (int shift = 28; shift >= 0; shift -= 4) {
52
+ out.push_back(hex_digits[(crc >> shift) & 0xFU]);
53
+ }
54
+ return out;
45
55
  }
46
56
  } // namespace
47
57
 
@@ -54,56 +64,82 @@ node_id::node_id(std::string node_uuid, std::string hostname, std::uint16_t port
54
64
  }
55
65
 
56
66
  auto
57
- node_id::id() const -> const std::string&
67
+ node_id::id() const noexcept -> const std::string&
58
68
  {
59
69
  return id_;
60
70
  }
61
71
 
62
72
  auto
63
- node_id::node_uuid() const -> const std::string&
73
+ node_id::node_uuid() const noexcept -> const std::string&
64
74
  {
65
75
  return node_uuid_;
66
76
  }
67
77
 
68
78
  auto
69
- node_id::hostname() const -> const std::string&
79
+ node_id::hostname() const noexcept -> const std::string&
70
80
  {
71
81
  return hostname_;
72
82
  }
73
83
 
74
84
  auto
75
- node_id::port() const -> std::uint16_t
85
+ node_id::port() const noexcept -> std::uint16_t
76
86
  {
77
87
  return port_;
78
88
  }
79
89
 
80
90
  node_id::
81
- operator bool() const
91
+ operator bool() const noexcept
82
92
  {
83
93
  return !id_.empty();
84
94
  }
85
95
 
86
96
  auto
87
- node_id::operator==(const node_id& other) const -> bool
97
+ node_id::operator==(const node_id& other) const noexcept -> bool
88
98
  {
89
99
  return id_ == other.id_;
90
100
  }
91
101
 
92
102
  auto
93
- node_id::operator!=(const node_id& other) const -> bool
103
+ node_id::operator!=(const node_id& other) const noexcept -> bool
94
104
  {
95
105
  return !(*this == other);
96
106
  }
97
107
 
98
108
  auto
99
- node_id::operator<(const node_id& other) const -> bool
109
+ node_id::operator<(const node_id& other) const noexcept -> bool
100
110
  {
101
111
  return id_ < other.id_;
102
112
  }
103
113
 
114
+ auto
115
+ node_id::operator<=(const node_id& other) const noexcept -> bool
116
+ {
117
+ return !(other < *this);
118
+ }
119
+
120
+ auto
121
+ node_id::operator>(const node_id& other) const noexcept -> bool
122
+ {
123
+ return other < *this;
124
+ }
125
+
126
+ auto
127
+ node_id::operator>=(const node_id& other) const noexcept -> bool
128
+ {
129
+ return !(*this < other);
130
+ }
131
+
104
132
  auto
105
133
  internal_node_id::build(std::string node_uuid, std::string hostname, std::uint16_t port) -> node_id
106
134
  {
135
+ // Preserve the "falsy = unknown" contract: a node that has neither a
136
+ // server-assigned UUID nor a usable hostname+port pair cannot be uniquely
137
+ // identified. Returning a default-constructed node_id keeps such an input
138
+ // falsy so request- and response-side identity construction agree on the
139
+ // same "empty" answer for not-yet-bound or KV-less topology entries.
140
+ if (node_uuid.empty() && (hostname.empty() || port == 0)) {
141
+ return {};
142
+ }
107
143
  return { std::move(node_uuid), std::move(hostname), port };
108
144
  }
109
145
 
@@ -45,8 +45,18 @@ observability_recorder::operation_span() -> const std::shared_ptr<couchbase::tra
45
45
  void
46
46
  observability_recorder::finish(const std::error_code ec)
47
47
  {
48
- metric_attributes_.ec = ec;
49
- meter_.lock()->record_value(std::move(metric_attributes_), start_time_);
48
+ // Precompute the standardized error type once here, so the recorder-cache lookup keyed on it does
49
+ // not build a string per comparison.
50
+ metric_attributes_.error_type = metrics::standardized_error_type(ec);
51
+ // meter_ is a weak_ptr. On the buffered path finish() always runs inside the io-thread execute
52
+ // completion, so the meter (owned by the cluster) is alive. The streaming path defers finish() to
53
+ // a user-controlled terminal — a stream handle dropped via its destructor/cancel() — which may
54
+ // run after the owning cluster, and its meter, is gone. Guard the lock so a late finish records
55
+ // nothing instead of dereferencing an expired weak_ptr. span_ holds a strong ref, so ending it is
56
+ // always safe.
57
+ if (const auto meter = meter_.lock()) {
58
+ meter->record_value(std::move(metric_attributes_), start_time_);
59
+ }
50
60
  span_->end();
51
61
  }
52
62