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
@@ -17,8 +17,6 @@
17
17
 
18
18
  #pragma once
19
19
 
20
- #include <couchbase/build_config.hxx>
21
-
22
20
  #include "core/cluster_credentials.hxx"
23
21
  #include "core/protocol/hello_feature.hxx"
24
22
  #include "core/response_handler.hxx"
@@ -45,18 +43,16 @@ class context;
45
43
  } // namespace ssl
46
44
  } // namespace asio
47
45
 
46
+ namespace couchbase::core::mcbp
47
+ {
48
+ class queue_request;
49
+ } // namespace couchbase::core::mcbp
50
+
48
51
  namespace couchbase::core
49
52
  {
50
53
  struct origin;
51
54
  class config_listener;
52
55
 
53
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
54
- namespace columnar
55
- {
56
- class background_bootstrap_listener;
57
- } // namespace columnar
58
- #endif
59
-
60
56
  namespace topology
61
57
  {
62
58
  struct configuration;
@@ -74,6 +70,9 @@ struct bootstrap_error;
74
70
  class bootstrap_state_listener;
75
71
  } // namespace impl
76
72
 
73
+ class app_telemetry_meter;
74
+ class app_telemetry_value_recorder;
75
+
77
76
  namespace io
78
77
  {
79
78
  class mcbp_session_impl;
@@ -108,7 +107,18 @@ public:
108
107
  std::optional<std::string> bucket_name = {},
109
108
  std::vector<protocol::hello_feature> known_features = {});
110
109
 
111
- [[nodiscard]] auto log_prefix() const -> const std::string&;
110
+ /**
111
+ * Constructs a session handle from an existing implementation pointer.
112
+ *
113
+ * Used internally by @ref mcbp_session_impl to create a lightweight snapshot of
114
+ * the session that can be passed to @ref response_handler::handle_response without
115
+ * transferring ownership of the live session. The snapshot shares the same
116
+ * @ref impl_ and therefore exposes the same connection metadata (remote/local
117
+ * address and port) at the time the response is delivered.
118
+ */
119
+ explicit mcbp_session(std::shared_ptr<mcbp_session_impl> impl);
120
+
121
+ [[nodiscard]] auto log_prefix() const -> std::string;
112
122
  [[nodiscard]] auto cancel(std::uint32_t opaque, std::error_code ec, retry_reason reason) -> bool;
113
123
  [[nodiscard]] auto is_stopped() const -> bool;
114
124
  [[nodiscard]] auto is_bootstrapped() const -> bool;
@@ -120,6 +130,11 @@ public:
120
130
  [[nodiscard]] auto supported_features() const -> std::vector<protocol::hello_feature>;
121
131
  [[nodiscard]] auto id() const -> const std::string&;
122
132
  [[nodiscard]] auto node_uuid() const -> const std::string&;
133
+ // Resolve the app_telemetry recorder for this connection, cached per connection and re-resolved
134
+ // only when the meter's generation changes (see app_telemetry_recorder_cache).
135
+ [[nodiscard]] auto app_telemetry_recorder(app_telemetry_meter& meter,
136
+ const std::string& bucket_name) const
137
+ -> std::shared_ptr<app_telemetry_value_recorder>;
123
138
  [[nodiscard]] auto remote_address() const -> std::string;
124
139
  [[nodiscard]] auto remote_hostname() const -> std::string;
125
140
  [[nodiscard]] auto remote_port() const -> std::uint16_t;
@@ -128,12 +143,10 @@ public:
128
143
  [[nodiscard]] auto bootstrap_hostname() const -> const std::string&;
129
144
  [[nodiscard]] auto bootstrap_port() const -> const std::string&;
130
145
  [[nodiscard]] auto bootstrap_port_number() const -> std::uint16_t;
131
- [[nodiscard]] auto last_bootstrap_error() && -> std::optional<impl::bootstrap_error>;
132
- [[nodiscard]] auto last_bootstrap_error() const& -> const std::optional<impl::bootstrap_error>&;
133
146
  [[nodiscard]] auto canonical_hostname() const -> const std::string&;
134
147
  [[nodiscard]] auto canonical_port_number() const -> std::uint16_t;
135
148
  void write_and_flush(std::vector<std::byte>&& buffer);
136
- void write_and_subscribe(const std::shared_ptr<mcbp::queue_request>&,
149
+ void write_and_subscribe(const std::shared_ptr<mcbp::queue_request>& request,
137
150
  const std::shared_ptr<response_handler>& handler);
138
151
  void write_and_subscribe(std::uint32_t opaque,
139
152
  std::vector<std::byte>&& data,
@@ -150,16 +163,12 @@ public:
150
163
  [[nodiscard]] auto diag_info() const -> diag::endpoint_diag_info;
151
164
  void on_configuration_update(std::shared_ptr<config_listener> handler);
152
165
  void ping(const std::shared_ptr<diag::ping_reporter>& handler,
153
- std::optional<std::chrono::milliseconds> = {}) const;
166
+ std::optional<std::chrono::milliseconds> timeout = {}) const;
154
167
  [[nodiscard]] auto supports_gcccp() const -> bool;
155
168
  [[nodiscard]] auto decode_error_code(std::uint16_t code)
156
169
  -> std::optional<key_value_error_map_info>;
157
170
  void handle_not_my_vbucket(const io::mcbp_message& msg) const;
158
171
  void update_collection_uid(const std::string& path, std::uint32_t uid);
159
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
160
- void add_background_bootstrap_listener(
161
- std::shared_ptr<columnar::background_bootstrap_listener> listener);
162
- #endif
163
172
 
164
173
  private:
165
174
  std::shared_ptr<mcbp_session_impl> impl_{ nullptr };
@@ -0,0 +1,145 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2024. 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 <array>
21
+ #include <cstddef>
22
+ #include <cstdint>
23
+ #include <unordered_map>
24
+ #include <utility>
25
+ #include <vector>
26
+
27
+ namespace couchbase::core::io
28
+ {
29
+ /**
30
+ * Maps a monotonically increasing 32-bit opaque to an in-flight handler without a per-operation
31
+ * heap allocation.
32
+ *
33
+ * The common case uses a fixed ring indexed by `opaque & mask`. Because the opaque increases by one
34
+ * per operation, in-flight operations occupy distinct slots as long as the range between the oldest
35
+ * and newest in-flight opaque stays within the ring; when it does not (a slow operation held open
36
+ * across a full lap of the ring, or opaque wraparound), the colliding entries spill to an overflow
37
+ * map, so lookups stay correct for any number of in-flight operations.
38
+ *
39
+ * Precondition: an opaque is unique while it is in flight — a value is never registered again
40
+ * before its handler has been taken. The single caller guarantees this by allocating a fresh opaque
41
+ * per operation, which is what lets insert() store into a free ring slot without also consulting
42
+ * the overflow map.
43
+ *
44
+ * Not thread-safe: callers serialize access.
45
+ */
46
+ template<typename Handler>
47
+ class opaque_ring_table
48
+ {
49
+ public:
50
+ // Register a handler for an opaque. Relies on the unique-in-flight precondition: because a fresh
51
+ // opaque is never already registered, it always finds either a free ring slot or (on a ring
52
+ // collision) a free overflow entry. If the target ring slot happens to already hold the same
53
+ // opaque, insert() keeps the existing handler -- but that is a cheap same-slot guard only; the
54
+ // overflow map is deliberately not consulted (that would add a lookup to every insert on the hot
55
+ // path). Re-registering an opaque that is still in flight therefore violates the precondition and
56
+ // is not a supported dedup: if the original spilled to overflow_ and its ring slot has since been
57
+ // reclaimed, a duplicate would land in the ring and strand the overflow copy.
58
+ //
59
+ // The handler is taken by rvalue reference and moved into storage exactly once (handlers are
60
+ // move-only and every call site passes an rvalue), avoiding the extra move a by-value parameter
61
+ // would add on the hot path.
62
+ void insert(std::uint32_t opaque, Handler&& handler);
63
+
64
+ // Remove and return the handler for an opaque, or a default-constructed (empty) handler if
65
+ // absent. The returned handler is the sole owner; dropping it strands the operation, hence
66
+ // [[nodiscard]].
67
+ [[nodiscard]] auto take(std::uint32_t opaque) -> Handler;
68
+
69
+ // Move out every registered (opaque, handler) pair and clear the table. The returned handlers are
70
+ // the sole remaining owners, hence [[nodiscard]].
71
+ [[nodiscard]] auto drain() -> std::vector<std::pair<std::uint32_t, Handler>>;
72
+
73
+ private:
74
+ static constexpr std::size_t ring_size = 512;
75
+ static constexpr std::uint32_t ring_mask = ring_size - 1;
76
+ // `opaque & ring_mask` is a valid modulo only when the size is a power of two.
77
+ static_assert((ring_size & (ring_size - 1)) == 0, "ring_size must be a power of two");
78
+
79
+ struct slot {
80
+ std::uint32_t opaque{ 0 };
81
+ bool occupied{ false };
82
+ Handler handler{};
83
+ };
84
+
85
+ std::array<slot, ring_size> ring_{};
86
+ std::unordered_map<std::uint32_t, Handler> overflow_{};
87
+ };
88
+
89
+ template<typename Handler>
90
+ void
91
+ opaque_ring_table<Handler>::insert(std::uint32_t opaque, Handler&& handler)
92
+ {
93
+ auto& s = ring_[opaque & ring_mask];
94
+ if (!s.occupied) {
95
+ s.opaque = opaque;
96
+ s.occupied = true;
97
+ s.handler = std::move(handler);
98
+ return;
99
+ }
100
+ if (s.opaque == opaque) {
101
+ return; // keep the existing handler (not expected under the unique-opaque precondition)
102
+ }
103
+ // the slot is held by a different in-flight opaque (ring collision) — spill to the overflow map
104
+ overflow_.try_emplace(opaque, std::move(handler));
105
+ }
106
+
107
+ template<typename Handler>
108
+ auto
109
+ opaque_ring_table<Handler>::take(std::uint32_t opaque) -> Handler
110
+ {
111
+ auto& s = ring_[opaque & ring_mask];
112
+ if (s.occupied && s.opaque == opaque) {
113
+ Handler h = std::move(s.handler);
114
+ s.handler = Handler{};
115
+ s.occupied = false;
116
+ return h;
117
+ }
118
+ if (auto it = overflow_.find(opaque); it != overflow_.end()) {
119
+ Handler h = std::move(it->second);
120
+ overflow_.erase(it);
121
+ return h;
122
+ }
123
+ return Handler{};
124
+ }
125
+
126
+ template<typename Handler>
127
+ auto
128
+ opaque_ring_table<Handler>::drain() -> std::vector<std::pair<std::uint32_t, Handler>>
129
+ {
130
+ std::vector<std::pair<std::uint32_t, Handler>> result;
131
+ result.reserve(ring_size + overflow_.size());
132
+ for (auto& s : ring_) {
133
+ if (s.occupied) {
134
+ result.emplace_back(s.opaque, std::move(s.handler));
135
+ s.handler = Handler{};
136
+ s.occupied = false;
137
+ }
138
+ }
139
+ for (auto& [opaque, h] : overflow_) {
140
+ result.emplace_back(opaque, std::move(h));
141
+ }
142
+ overflow_.clear();
143
+ return result;
144
+ }
145
+ } // namespace couchbase::core::io
@@ -18,6 +18,7 @@
18
18
  #pragma once
19
19
 
20
20
  #include "core/logger/logger.hxx"
21
+ #include "core/logger/redaction.hxx"
21
22
  #include "core/protocol/client_opcode_fmt.hxx"
22
23
 
23
24
  #include <couchbase/best_effort_retry_strategy.hxx>
@@ -33,8 +34,8 @@ namespace priv
33
34
  {
34
35
  template<class Command>
35
36
  auto
36
- cap_duration(std::chrono::milliseconds uncapped,
37
- std::shared_ptr<Command> command) -> std::chrono::milliseconds
37
+ cap_duration(std::chrono::milliseconds uncapped, std::shared_ptr<Command> command)
38
+ -> std::chrono::milliseconds
38
39
  {
39
40
  auto theoretical_deadline = std::chrono::steady_clock::now() + uncapped;
40
41
  auto absolute_deadline = command->deadline.expiry();
@@ -63,11 +64,11 @@ retry_with_duration(std::shared_ptr<Manager> manager,
63
64
  manager->log_prefix(),
64
65
  decltype(command->request)::encoded_request_type::body_type::opcode,
65
66
  duration.count(),
66
- command->id_,
67
+ command->id(),
67
68
  command->request.partition,
68
69
  reason,
69
70
  command->request.retries.retry_attempts(),
70
- command->session_ ? command->session_->remote_address() : "");
71
+ logger::system_data(command->session_ ? command->session_->remote_address() : ""));
71
72
  manager->schedule_for_retry(command, duration);
72
73
  }
73
74
 
@@ -98,7 +99,7 @@ maybe_retry(std::shared_ptr<Manager> manager,
98
99
  CB_LOG_TRACE(R"({} not retrying operation {} (id="{}", reason={}, attempts={}, ec={} ({})))",
99
100
  manager->log_prefix(),
100
101
  decltype(command->request)::encoded_request_type::body_type::opcode,
101
- command->id_,
102
+ command->id(),
102
103
  reason,
103
104
  command->request.retries.retry_attempts(),
104
105
  ec.value(),
@@ -0,0 +1,50 @@
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 <system_error>
21
+
22
+ namespace couchbase::core::io
23
+ {
24
+ // Which timeout a whole-stream deadline reports when it fires. The two are not interchangeable: a
25
+ // retry layer may replay a request that definitely did not apply, and must not replay one that may
26
+ // have partially executed. A deadline has no other terminal, so no other value exists.
27
+ enum class deadline_terminal {
28
+ // The request is read-only, so the server cannot have applied it.
29
+ unambiguous,
30
+ // The request may have mutated state before the deadline closed the body.
31
+ ambiguous,
32
+ };
33
+
34
+ // Whether a set_deadline call installed anything.
35
+ enum class deadline_state {
36
+ // Installed. The body closes at the deadline unless something ends it first.
37
+ armed,
38
+ // Nothing was installed, and nothing needs to be: the body has already ended, by expiry, by
39
+ // cancellation, or by having been read to completion. A replayed buffered response starts here.
40
+ //
41
+ // This is not end-of-stream. A consumer pulling rows reaches it before its last row, because
42
+ // the body ends when its bytes are read and the rows parsed from them are handed out
43
+ // afterwards. A caller arming per operation sees it for the final rows of every stream.
44
+ body_already_ended,
45
+ };
46
+
47
+ // The error_code `terminal` is reported as.
48
+ [[nodiscard]] auto
49
+ terminal_error_code(deadline_terminal terminal) -> std::error_code;
50
+ } // namespace couchbase::core::io
@@ -18,16 +18,22 @@
18
18
  #include "streams.hxx"
19
19
 
20
20
  #include "core/platform/uuid.h"
21
+ #include "core/utils/process_id.hxx"
21
22
 
22
23
  #include <asio.hpp>
23
24
  #include <asio/error.hpp>
24
25
  #include <asio/ssl.hpp>
26
+ #include <asio/ssl/host_name_verification.hpp>
27
+ // ERR_get_error()/SSL_* come from the OpenSSL or BoringSSL headers pulled in by
28
+ // <asio/ssl.hpp>; do not include <openssl/*.h> directly so the static BoringSSL
29
+ // build (which ships its own headers) keeps working, matching core/io/mcbp_session.cxx.
25
30
 
31
+ #include <algorithm>
26
32
  #include <cerrno>
33
+ #include <memory>
34
+ #include <mutex>
27
35
 
28
- #if defined(_WIN32)
29
- #include <process.h>
30
- #else
36
+ #if !defined(_WIN32)
31
37
  #include <unistd.h>
32
38
  #endif
33
39
 
@@ -35,16 +41,6 @@ namespace couchbase::core::io
35
41
  {
36
42
  namespace
37
43
  {
38
- auto
39
- current_process_id() -> long long
40
- {
41
- #if defined(_WIN32)
42
- return static_cast<long long>(::_getpid());
43
- #else
44
- return static_cast<long long>(::getpid());
45
- #endif
46
- }
47
-
48
44
  /**
49
45
  * Release a socket this process inherited across fork(2), without disturbing the
50
46
  * connection the parent is still using.
@@ -65,10 +61,12 @@ current_process_id() -> long long
65
61
  * release() also completes any outstanding asynchronous operations on this socket
66
62
  * with operation_aborted, which is what we want: those reads belong to the parent.
67
63
  */
68
- // Takes the basic_socket base rather than tcp::socket so the same code serves both
69
- // the plain socket and a TLS stream's lowest_layer(), whose type is that base.
64
+ // Takes the basic_socket base rather than a concrete socket type, so the same code serves the
65
+ // plain socket, a TLS stream's lowest_layer(), whose type is that base, and the DNS client's
66
+ // sockets.
67
+ template<typename Protocol>
70
68
  void
71
- detach_inherited_socket(asio::basic_socket<asio::ip::tcp>& socket, asio::error_code& ec)
69
+ detach_inherited_socket(asio::basic_socket<Protocol>& socket, asio::error_code& ec)
72
70
  {
73
71
  if (!socket.is_open()) {
74
72
  ec = asio::error::bad_descriptor;
@@ -140,11 +138,127 @@ configure_tls_handshake(asio::ssl::stream<asio::ip::tcp::socket>& stream,
140
138
  return ec;
141
139
  }
142
140
 
141
+ // The sockets of one io_context, each with the process that opened it. Held weakly: a socket also
142
+ // lives in the handlers that use it, such as a close posted before a fork, and is found there too.
143
+ class inherited_stream_registry : public asio::execution_context::service
144
+ {
145
+ public:
146
+ static inline asio::execution_context::id id{};
147
+
148
+ explicit inherited_stream_registry(asio::execution_context& ctx)
149
+ : asio::execution_context::service(ctx)
150
+ {
151
+ }
152
+
153
+ void add(const std::shared_ptr<asio::ip::tcp::socket>& socket)
154
+ {
155
+ const std::scoped_lock lock(mutex_);
156
+ prune();
157
+ plain_.push_back({ socket, utils::current_process_id() });
158
+ }
159
+
160
+ void add(const std::shared_ptr<asio::ssl::stream<asio::ip::tcp::socket>>& stream)
161
+ {
162
+ const std::scoped_lock lock(mutex_);
163
+ prune();
164
+ tls_.push_back({ stream, utils::current_process_id() });
165
+ }
166
+
167
+ void add(const std::shared_ptr<asio::ip::udp::socket>& socket)
168
+ {
169
+ const std::scoped_lock lock(mutex_);
170
+ prune();
171
+ udp_.push_back({ socket, utils::current_process_id() });
172
+ }
173
+
174
+ // The detach result needs no handling. asio's reactive release() fails only on a socket that is
175
+ // not open, which is skipped here, and once it succeeds the socket is out of the reactor with its
176
+ // operations aborted, so a failed close(2) cannot let anything run on the parent's connection.
177
+ void detach_inherited()
178
+ {
179
+ const auto pid = utils::current_process_id();
180
+ const std::scoped_lock lock(mutex_);
181
+ for (const auto& [weak, owner_pid] : plain_) {
182
+ if (auto socket = weak.lock(); socket && owner_pid != pid && socket->is_open()) {
183
+ asio::error_code ec{};
184
+ detach_inherited_socket(*socket, ec);
185
+ }
186
+ }
187
+ for (const auto& [weak, owner_pid] : tls_) {
188
+ if (auto stream = weak.lock();
189
+ stream && owner_pid != pid && stream->lowest_layer().is_open()) {
190
+ asio::error_code ec{};
191
+ detach_inherited_socket(stream->lowest_layer(), ec);
192
+ }
193
+ }
194
+ for (const auto& [weak, owner_pid] : udp_) {
195
+ if (auto socket = weak.lock(); socket && owner_pid != pid && socket->is_open()) {
196
+ asio::error_code ec{};
197
+ detach_inherited_socket(*socket, ec);
198
+ }
199
+ }
200
+ prune();
201
+ }
202
+
203
+ private:
204
+ template<typename Stream>
205
+ struct entry {
206
+ std::weak_ptr<Stream> stream;
207
+ std::int64_t owner_pid;
208
+ };
209
+
210
+ void shutdown() override
211
+ {
212
+ }
213
+
214
+ void prune()
215
+ {
216
+ const auto expired = [](const auto& e) {
217
+ return e.stream.expired();
218
+ };
219
+ plain_.erase(std::remove_if(plain_.begin(), plain_.end(), expired), plain_.end());
220
+ tls_.erase(std::remove_if(tls_.begin(), tls_.end(), expired), tls_.end());
221
+ udp_.erase(std::remove_if(udp_.begin(), udp_.end(), expired), udp_.end());
222
+ }
223
+
224
+ std::mutex mutex_{};
225
+ std::vector<entry<asio::ip::tcp::socket>> plain_{};
226
+ std::vector<entry<asio::ssl::stream<asio::ip::tcp::socket>>> tls_{};
227
+ std::vector<entry<asio::ip::udp::socket>> udp_{};
228
+ };
229
+
230
+ void
231
+ detach_inherited_sockets(asio::io_context& ctx)
232
+ {
233
+ if (asio::has_service<inherited_stream_registry>(ctx)) {
234
+ asio::use_service<inherited_stream_registry>(static_cast<asio::execution_context&>(ctx))
235
+ .detach_inherited();
236
+ }
237
+ }
238
+
239
+ void
240
+ register_inherited_socket(asio::io_context& ctx,
241
+ const std::shared_ptr<asio::ip::tcp::socket>& socket)
242
+ {
243
+ asio::use_service<inherited_stream_registry>(static_cast<asio::execution_context&>(ctx))
244
+ .add(socket);
245
+ }
246
+
247
+ void
248
+ register_inherited_socket(asio::io_context& ctx,
249
+ const std::shared_ptr<asio::ip::udp::socket>& socket)
250
+ {
251
+ asio::use_service<inherited_stream_registry>(static_cast<asio::execution_context&>(ctx))
252
+ .add(socket);
253
+ }
254
+
143
255
  stream_impl::stream_impl(asio::io_context& ctx, bool is_tls)
144
- : strand_(asio::make_strand(ctx))
256
+ : inherited_(
257
+ asio::use_service<inherited_stream_registry>(static_cast<asio::execution_context&>(ctx)))
258
+ , strand_(asio::make_strand(ctx))
145
259
  , tls_(is_tls)
146
260
  , id_(uuid::to_string(uuid::random()))
147
- , owner_pid_(current_process_id())
261
+ , owner_pid_(utils::current_process_id())
148
262
  {
149
263
  }
150
264
 
@@ -164,6 +278,7 @@ plain_stream_impl::plain_stream_impl(asio::io_context& ctx)
164
278
  : stream_impl(ctx, false)
165
279
  , stream_(std::make_shared<asio::ip::tcp::socket>(strand_))
166
280
  {
281
+ inherited_.add(stream_);
167
282
  }
168
283
 
169
284
  auto
@@ -201,7 +316,7 @@ plain_stream_impl::close(utils::movable_function<void(std::error_code)>&& handle
201
316
  // Decide here rather than when this was posted: io_.stop() at fork_prepare
202
317
  // does not drain the queue, so a close queued before the fork runs in the
203
318
  // child once the io_context is restarted, and there the answer differs.
204
- if (owner_pid != current_process_id()) {
319
+ if (owner_pid != utils::current_process_id()) {
205
320
  detach_inherited_socket(*stream, ec);
206
321
  } else {
207
322
  stream->shutdown(asio::socket_base::shutdown_both, ec);
@@ -225,11 +340,13 @@ plain_stream_impl::set_options()
225
340
  void
226
341
  plain_stream_impl::async_connect(
227
342
  const asio::ip::tcp::resolver::results_type::endpoint_type& endpoint,
343
+ const std::string& /* hostname */,
228
344
  utils::movable_function<void(std::error_code)>&& handler)
229
345
  {
230
346
  if (!stream_) {
231
347
  id_ = uuid::to_string(uuid::random());
232
348
  stream_ = std::make_shared<asio::ip::tcp::socket>(strand_);
349
+ inherited_.add(stream_);
233
350
  }
234
351
  return stream_->async_connect(endpoint,
235
352
  [stream = stream_, handler = std::move(handler)](auto ec) {
@@ -271,6 +388,7 @@ tls_stream_impl::tls_stream_impl(asio::io_context& ctx, tls_context_provider& tl
271
388
  std::make_shared<asio::ssl::stream<asio::ip::tcp::socket>>(asio::ip::tcp::socket(strand_),
272
389
  *tls_.get_ctx()))
273
390
  {
391
+ inherited_.add(stream_);
274
392
  }
275
393
 
276
394
  auto
@@ -307,7 +425,7 @@ tls_stream_impl::close(utils::movable_function<void(std::error_code)>&& handler)
307
425
  asio::error_code ec{};
308
426
  // See plain_stream_impl::close(): the inheritance test belongs here, not at
309
427
  // post time.
310
- if (owner_pid != current_process_id()) {
428
+ if (owner_pid != utils::current_process_id()) {
311
429
  detach_inherited_socket(stream->lowest_layer(), ec);
312
430
  } else {
313
431
  stream->lowest_layer().shutdown(asio::socket_base::shutdown_both, ec);
@@ -330,21 +448,33 @@ tls_stream_impl::set_options()
330
448
 
331
449
  void
332
450
  tls_stream_impl::async_connect(const asio::ip::tcp::resolver::results_type::endpoint_type& endpoint,
451
+ const std::string& hostname,
333
452
  utils::movable_function<void(std::error_code)>&& handler)
334
453
  {
335
454
  if (!stream_) {
336
455
  id_ = uuid::to_string(uuid::random());
337
456
  stream_ = std::make_shared<asio::ssl::stream<asio::ip::tcp::socket>>(
338
457
  asio::ip::tcp::socket(strand_), *tls_.get_ctx());
458
+ inherited_.add(stream_);
339
459
  }
340
460
  return stream_->lowest_layer().async_connect(
341
- endpoint, [stream = stream_, handler = std::move(handler)](std::error_code ec_connect) mutable {
461
+ endpoint,
462
+ // hostname is copied into the closure (owning std::string) so the async
463
+ // handler does not depend on the caller's argument lifetime.
464
+ [stream = stream_, hostname = hostname, handler = std::move(handler)](
465
+ std::error_code ec_connect) mutable {
342
466
  if (ec_connect == asio::error::operation_aborted) {
343
467
  return;
344
468
  }
345
469
  if (ec_connect) {
346
470
  return handler(ec_connect);
347
471
  }
472
+ // Verify the server's identity against `hostname` (SAN/CN) and set SNI
473
+ // before the handshake. Without this, verify_peer only checks that the
474
+ // certificate chains to a trusted CA, not that it identifies this host.
475
+ if (auto ec_identity = configure_tls_handshake(*stream, hostname); ec_identity) {
476
+ return handler(ec_identity);
477
+ }
348
478
  stream->async_handshake(
349
479
  asio::ssl::stream_base::client,
350
480
  [stream, handler = std::move(handler)](std::error_code ec_handshake) mutable {