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
@@ -0,0 +1,180 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2026-Present Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #pragma once
19
+
20
+ #include "core/cluster.hxx"
21
+ #include "core/topology/configuration.hxx"
22
+
23
+ #include <couchbase/error.hxx>
24
+ #include <couchbase/error_codes.hxx>
25
+
26
+ #include <asio/error.hpp>
27
+ #include <asio/io_context.hpp>
28
+ #include <asio/post.hpp>
29
+ #include <asio/steady_timer.hpp>
30
+
31
+ #include <atomic>
32
+ #include <chrono>
33
+ #include <memory>
34
+ #include <string>
35
+ #include <system_error>
36
+ #include <utility>
37
+
38
+ namespace couchbase::core::impl
39
+ {
40
+ /**
41
+ * Async scaffold shared between @c collection_impl::node_id_for and
42
+ * @c collection_impl::node_ids — and any future client-side API that needs
43
+ * to wait for a bucket configuration with a hard timeout.
44
+ *
45
+ * The scaffold owns the atomic-done guard, the timer, and the asio::post
46
+ * back to the io_context. Callers supply a resolver that maps a topology
47
+ * snapshot to a (error_code, payload) pair; the user handler is invoked
48
+ * with (error, payload) exactly once — including on io_context shutdown,
49
+ * where a fail-closed destructor backstop completes the handler with
50
+ * @c errc::network::cluster_closed rather than leaking a dropped completion
51
+ * (which would surface as a broken_promise to a std::future caller or a
52
+ * permanent hang to a callback caller).
53
+ *
54
+ * Thread-safety: the SDK's core io_context is single-threaded for KV work
55
+ * in the current release. The timer and atomic-done dance are safe under
56
+ * that assumption. If the io_context is ever multi-threaded, both
57
+ * @c timer.cancel() and the handler dispatch must be wrapped in a strand
58
+ * because @c asio::steady_timer is not thread-safe.
59
+ *
60
+ * @tparam Payload The success-path payload type (e.g. @c node_id or
61
+ * @c std::vector<node_id>). Must be default-
62
+ * constructible (used for the timeout / error branches)
63
+ * and move-constructible.
64
+ * @tparam Handler Callable accepting @c (couchbase::error, Payload).
65
+ * @tparam Resolver Callable accepting
66
+ * @c const std::shared_ptr<topology::configuration>&
67
+ * and returning @c std::pair<std::error_code, Payload>.
68
+ * Invoked at most once, on the config-callback path
69
+ * when @c ec is falsy.
70
+ */
71
+ template<typename Payload, typename Handler, typename Resolver>
72
+ void
73
+ with_bucket_config_or_timeout(const couchbase::core::cluster& core,
74
+ const std::string& bucket_name,
75
+ std::chrono::milliseconds timeout,
76
+ Handler handler,
77
+ Resolver resolver)
78
+ {
79
+ struct state {
80
+ std::atomic<bool> done{ false };
81
+ std::atomic<bool> delivered{ false };
82
+ asio::steady_timer timer;
83
+ Handler handler;
84
+
85
+ state(asio::io_context& ioc, Handler&& h)
86
+ : timer{ ioc }
87
+ , handler{ std::move(h) }
88
+ {
89
+ }
90
+
91
+ // Invoke the user handler at most once. The `done` flag arbitrates the
92
+ // timer-vs-config race; `delivered` guards the handler call itself so the
93
+ // destructor can fail closed (below) without ever risking a double call.
94
+ void deliver(couchbase::error err, Payload payload)
95
+ {
96
+ if (!delivered.exchange(true, std::memory_order_acq_rel)) {
97
+ handler(std::move(err), std::move(payload));
98
+ }
99
+ }
100
+
101
+ // Fail-closed backstop. If every async continuation is torn down without
102
+ // the handler having run — io_context stopped on shutdown drops both the
103
+ // pending timer wait and any deferred asio::post — complete here so a
104
+ // std::future caller never observes broken_promise and a callback caller
105
+ // never hangs. By the time the destructor runs the last shared_ptr
106
+ // reference is gone, so no other thread can be in deliver() concurrently.
107
+ // The handler is user code; a destructor must not let an exception escape.
108
+ ~state()
109
+ {
110
+ try {
111
+ deliver(couchbase::error{ errc::network::cluster_closed }, Payload{});
112
+ } catch (...) { // NOLINT(bugprone-empty-catch)
113
+ }
114
+ }
115
+ };
116
+
117
+ auto shared = std::make_shared<state>(core.io_context(), std::move(handler));
118
+ // Capture the io_context by pointer value (not by reference to a local
119
+ // reference variable): core and its io_context outlive this operation, so
120
+ // the pointer stays valid even though the bucket-config callback may run
121
+ // after this function has returned.
122
+ auto* io_context = &core.io_context();
123
+
124
+ shared->timer.expires_after(timeout);
125
+ shared->timer.async_wait([shared](std::error_code timer_ec) -> void {
126
+ if (timer_ec == asio::error::operation_aborted) {
127
+ return;
128
+ }
129
+ // acq_rel matches the other writer (config callback): the only thing
130
+ // we synchronise on is whether we won the race to fire the handler.
131
+ if (shared->done.exchange(true, std::memory_order_acq_rel)) {
132
+ return;
133
+ }
134
+ shared->deliver(couchbase::error{ errc::common::unambiguous_timeout }, Payload{});
135
+ });
136
+
137
+ core.with_bucket_configuration(
138
+ bucket_name,
139
+ // Capture a weak_ptr, not a strong one. with_bucket_configuration() queues
140
+ // this callback in the bucket's deferred-commands list until a config
141
+ // arrives; if the timeout fires first and a config never arrives, a strong
142
+ // capture would pin the user handler, promise and timer in that queue
143
+ // indefinitely. The pending timer owns the state while we wait, so a config
144
+ // that arrives before the timeout still locks successfully; once the
145
+ // timeout has fired the state is released and this callback is a no-op.
146
+ [weak = std::weak_ptr<state>(shared), io_context, resolver = std::move(resolver)](
147
+ std::error_code ec,
148
+ const std::shared_ptr<core::topology::configuration>& config) mutable -> void {
149
+ auto shared = weak.lock();
150
+ if (!shared || shared->done.exchange(true, std::memory_order_acq_rel)) {
151
+ return;
152
+ }
153
+
154
+ std::error_code result_ec = ec;
155
+ Payload payload{};
156
+ if (!ec) {
157
+ std::tie(result_ec, payload) = resolver(config);
158
+ }
159
+
160
+ // Hop back onto the io_context before touching the timer or invoking
161
+ // the user handler. with_bucket_configuration() invokes its callback
162
+ // synchronously on the caller's thread when the bucket configuration
163
+ // is already cached (see bucket.cxx). Two things must therefore not
164
+ // happen inline here:
165
+ // 1. timer.cancel() — asio::steady_timer is not thread-safe and is
166
+ // bound to core.io_context(); cancelling it from an arbitrary
167
+ // application thread races with the io_context processing the
168
+ // pending async_wait. It must run on the timer's executor.
169
+ // 2. the user handler — it reasonably expects an async hop and may
170
+ // itself re-enter the SDK.
171
+ // The done flag above is atomic, so the race is already decided; the
172
+ // deferred cancel() is a harmless no-op if the timer has since fired
173
+ // (its handler short-circuits on operation_aborted / done).
174
+ asio::post(*io_context, [shared, result_ec, payload = std::move(payload)]() mutable {
175
+ shared->timer.cancel();
176
+ shared->deliver(couchbase::error{ result_ec }, std::move(payload));
177
+ });
178
+ });
179
+ }
180
+ } // namespace couchbase::core::impl
@@ -17,9 +17,9 @@
17
17
 
18
18
  #pragma once
19
19
 
20
- #include "core/impl/get_replica.hxx"
21
20
  #include "core/impl/lookup_in_replica.hxx"
22
21
  #include "core/operations/document_get.hxx"
22
+ #include "core/operations/document_get_replica.hxx"
23
23
  #include "core/operations/document_lookup_in.hxx"
24
24
  #include "core/operations/operation_traits.hxx"
25
25
  #include "core/utils/movable_function.hxx"
@@ -35,7 +35,7 @@ class cancellation_token
35
35
  public:
36
36
  void setup(utils::movable_function<void()> cancel_fn)
37
37
  {
38
- const std::lock_guard lock(mutex_);
38
+ const std::scoped_lock lock(mutex_);
39
39
  if (cancelled_) {
40
40
  cancel_fn();
41
41
  return;
@@ -47,7 +47,7 @@ public:
47
47
  {
48
48
  utils::movable_function<void()> fn{};
49
49
  {
50
- const std::lock_guard lock(mutex_);
50
+ const std::scoped_lock lock(mutex_);
51
51
  cancelled_ = true;
52
52
  fn = std::move(cancel_fn_);
53
53
  }
@@ -72,4 +72,12 @@ template<typename kv_operation>
72
72
  struct operations::is_cancellable_operation<impl::with_cancellation<kv_operation>>
73
73
  : public std::true_type {
74
74
  };
75
+
76
+ // Wrapping must not change how a request is routed: the wrapper inherits the
77
+ // operation, so a trait left behind here would silently drop the wrapped
78
+ // request back onto plain vbucket-map routing.
79
+ template<typename kv_operation>
80
+ struct operations::resolves_own_route<impl::with_cancellation<kv_operation>>
81
+ : public operations::resolves_own_route<kv_operation> {
82
+ };
75
83
  } // namespace couchbase::core
@@ -60,8 +60,7 @@ configuration_belongs_to_session(const std::optional<std::string>& config_bucket
60
60
  }
61
61
 
62
62
  // Stage 3 — both sides name a bucket: accept only if it is the *same* bucket.
63
- // Reaching here guarantees both optionals are engaged, so value() is safe.
64
63
  // Guards against applying another bucket's map to this session.
65
- return config_bucket.value() == session_bucket.value();
64
+ return config_bucket == session_bucket;
66
65
  }
67
66
  } // namespace couchbase::core::io
@@ -17,8 +17,11 @@
17
17
 
18
18
  #include "dns_client.hxx"
19
19
 
20
+ #include "core/io/streams.hxx"
20
21
  #include "core/logger/logger.hxx"
22
+ #include "core/logger/redaction.hxx"
21
23
  #include "core/utils/join_strings.hxx"
24
+ #include "core/utils/process_id.hxx"
22
25
  #include "dns_codec.hxx"
23
26
  #include "dns_config.hxx"
24
27
 
@@ -37,6 +40,29 @@
37
40
 
38
41
  namespace couchbase::core::io::dns
39
42
  {
43
+ namespace
44
+ {
45
+ auto
46
+ rcode_description(response_code rcode) -> const char*
47
+ {
48
+ switch (rcode) {
49
+ case response_code::no_error:
50
+ return "NOERROR";
51
+ case response_code::format_error:
52
+ return "FORMERR (server could not interpret the query)";
53
+ case response_code::server_failure:
54
+ return "SERVFAIL (server failure — DNS resolver may not support SRV queries)";
55
+ case response_code::name_error:
56
+ return "NXDOMAIN (name does not exist — no SRV record registered for this hostname)";
57
+ case response_code::not_implemented:
58
+ return "NOTIMP (query type not implemented by the server)";
59
+ case response_code::refused:
60
+ return "REFUSED (server refused the query — policy restriction)";
61
+ }
62
+ return "UNKNOWN";
63
+ }
64
+ } // namespace
65
+
40
66
  class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
41
67
  {
42
68
  public:
@@ -47,7 +73,8 @@ public:
47
73
  asio::ip::address address,
48
74
  std::uint16_t port,
49
75
  utils::movable_function<void(couchbase::core::io::dns::dns_srv_response&& resp)> handler)
50
- : deadline_(ctx)
76
+ : ctx_(ctx)
77
+ , deadline_(ctx)
51
78
  , udp_deadline_(ctx)
52
79
  , udp_(ctx)
53
80
  , tcp_(ctx)
@@ -73,13 +100,19 @@ public:
73
100
 
74
101
  void execute(std::chrono::milliseconds total_timeout, std::chrono::milliseconds udp_timeout)
75
102
  {
76
- CB_LOG_TRACE("Query DNS-SRV (UDP) address=\"{}:{}\", udp_timeout={}, total_timeout={}",
77
- address_.to_string(),
78
- port_,
103
+ CB_LOG_TRACE("Query DNS-SRV (UDP) address=\"{}\", udp_timeout={}, total_timeout={}",
104
+ logger::system_data(nameserver_address()),
79
105
  udp_timeout,
80
106
  total_timeout);
81
107
  const asio::ip::udp::endpoint endpoint(address_, port_);
82
108
  udp_.open(endpoint.protocol());
109
+ // A process forked while this lookup is pending inherits both sockets. Registering them lets
110
+ // the forked process's fixup release them, so it cannot read the response this process is
111
+ // waiting for.
112
+ register_inherited_socket(ctx_,
113
+ std::shared_ptr<asio::ip::udp::socket>(shared_from_this(), &udp_));
114
+ register_inherited_socket(ctx_,
115
+ std::shared_ptr<asio::ip::tcp::socket>(shared_from_this(), &tcp_));
83
116
  CB_LOG_PROTOCOL("[DNS, UDP, OUT] host=\"{}\", port={}, buffer_size={}{:a}",
84
117
  address_.to_string(),
85
118
  port_,
@@ -100,9 +133,8 @@ public:
100
133
  return;
101
134
  }
102
135
  CB_LOG_DEBUG(
103
- "DNS UDP write operation has got error, retrying with TCP, address=\"{}:{}\", ec={}",
104
- self->address_.to_string(),
105
- self->port_,
136
+ "DNS UDP write operation has got error, retrying with TCP, address=\"{}\", ec={}",
137
+ logger::system_data(self->nameserver_address()),
106
138
  ec1.message());
107
139
  return self->retry_with_tcp();
108
140
  }
@@ -126,20 +158,26 @@ public:
126
158
  return;
127
159
  }
128
160
  CB_LOG_DEBUG(
129
- "DNS UDP read operation has got error, retrying with TCP, address=\"{}:{}\", ec={}",
130
- self->address_.to_string(),
131
- self->port_,
161
+ "DNS UDP read operation has got error, retrying with TCP, address=\"{}\", ec={}",
162
+ logger::system_data(self->nameserver_address()),
132
163
  ec2.message());
133
164
  return self->retry_with_tcp();
134
165
  }
135
166
  self->recv_buf_.resize(bytes_transferred);
136
167
  const dns_message message = dns_codec::decode(self->recv_buf_);
137
168
  if (message.header.flags.tc == truncation::yes) {
138
- self->udp_.close();
169
+ std::error_code ignored{};
170
+ self->udp_.close(ignored);
139
171
  CB_LOG_DEBUG("DNS UDP read operation returned truncated response, retrying with TCP");
140
172
  return self->retry_with_tcp();
141
173
  }
142
174
  self->deadline_.cancel();
175
+ if (message.header.flags.rcode != response_code::no_error) {
176
+ CB_LOG_WARNING("DNS UDP response for \"{}\" returned non-zero RCODE: {} ({})",
177
+ logger::system_data(self->nameserver_address()),
178
+ static_cast<int>(message.header.flags.rcode),
179
+ rcode_description(message.header.flags.rcode));
180
+ }
143
181
  dns_srv_response resp{ ec2 };
144
182
  resp.targets.reserve(message.answers.size());
145
183
  for (const auto& answer : message.answers) {
@@ -147,7 +185,7 @@ public:
147
185
  utils::join_strings(answer.target.labels, "."), answer.port });
148
186
  }
149
187
  CB_LOG_DEBUG("DNS UDP returned {} records", resp.targets.size());
150
- return self->handler_(std::move(resp));
188
+ return self->complete(std::move(resp));
151
189
  });
152
190
  });
153
191
  udp_deadline_.expires_after(udp_timeout);
@@ -155,11 +193,11 @@ public:
155
193
  if (ec == asio::error::operation_aborted) {
156
194
  return;
157
195
  }
158
- self->udp_.cancel();
196
+ std::error_code ignored{};
197
+ self->udp_.cancel(ignored);
159
198
  CB_LOG_DEBUG("DNS UDP deadline has been reached, cancelling UDP operation and fall back to "
160
- "TCP, address=\"{}:{}\"",
161
- self->address_.to_string(),
162
- self->port_);
199
+ "TCP, address=\"{}\"",
200
+ logger::system_data(self->nameserver_address()));
163
201
  return self->retry_with_tcp();
164
202
  });
165
203
 
@@ -169,29 +207,52 @@ public:
169
207
  return;
170
208
  }
171
209
  CB_LOG_DEBUG("DNS deadline has been reached, cancelling in-flight operations "
172
- "(tcp.is_open={}, address=\"{}:{}\")",
210
+ "(tcp.is_open={}, address=\"{}\")",
173
211
  self->tcp_.is_open(),
174
- self->address_.to_string(),
175
- self->port_);
176
- self->udp_.cancel();
212
+ logger::system_data(self->nameserver_address()));
213
+ std::error_code ignored{};
214
+ self->udp_.cancel(ignored);
177
215
  if (self->tcp_.is_open()) {
178
- self->tcp_.cancel();
216
+ self->tcp_.cancel(ignored);
179
217
  }
180
- return self->handler_({ errc::common::unambiguous_timeout });
218
+ return self->complete({ errc::common::unambiguous_timeout });
181
219
  });
182
220
  }
183
221
 
184
222
  private:
223
+ // A lookup that a forked process inherited belongs to the process that started it. The child
224
+ // fixup releases its sockets, which fails their pending operations; neither those failures nor
225
+ // its timers may retry or complete the lookup in that process.
226
+ [[nodiscard]] auto inherited() const -> bool
227
+ {
228
+ return owner_pid_ != utils::current_process_id();
229
+ }
230
+
231
+ void complete(dns_srv_response&& resp)
232
+ {
233
+ if (inherited()) {
234
+ return;
235
+ }
236
+ handler_(std::move(resp));
237
+ }
238
+
239
+ [[nodiscard]] auto nameserver_address() const -> std::string
240
+ {
241
+ return fmt::format("{}:{}", address_.to_string(), port_);
242
+ }
243
+
185
244
  void retry_with_tcp()
186
245
  {
246
+ if (inherited()) {
247
+ return;
248
+ }
187
249
  if (bool expected_state{ false };
188
250
  !retrying_with_tcp_.compare_exchange_strong(expected_state, true)) {
189
251
  return;
190
252
  }
191
253
 
192
- CB_LOG_TRACE("Query DNS-SRV (TCP) address=\"{}:{}\", time_left={}",
193
- address_.to_string(),
194
- port_,
254
+ CB_LOG_TRACE("Query DNS-SRV (TCP) address=\"{}\", time_left={}",
255
+ logger::system_data(nameserver_address()),
195
256
  std::chrono::duration_cast<std::chrono::milliseconds>(
196
257
  deadline_.expiry() - std::chrono::steady_clock::now()));
197
258
 
@@ -205,11 +266,10 @@ private:
205
266
  return;
206
267
  }
207
268
  self->deadline_.cancel();
208
- CB_LOG_DEBUG("DNS TCP connection has been aborted, address=\"{}:{}\", ec={}",
209
- self->address_.to_string(),
210
- self->port_,
269
+ CB_LOG_DEBUG("DNS TCP connection has been aborted, address=\"{}\", ec={}",
270
+ logger::system_data(self->nameserver_address()),
211
271
  ec1.message());
212
- return self->handler_({ ec1 });
272
+ return self->complete({ ec1 });
213
273
  }
214
274
  auto send_size = static_cast<std::uint16_t>(self->send_buf_.size());
215
275
  self->send_buf_.insert(self->send_buf_.begin(), static_cast<std::uint8_t>(send_size & 0xffU));
@@ -232,12 +292,11 @@ private:
232
292
  if (ec2 == asio::error::operation_aborted) {
233
293
  return;
234
294
  }
235
- CB_LOG_DEBUG("DNS TCP write operation has been aborted, address=\"{}:{}\", ec={}",
236
- self->address_.to_string(),
237
- self->port_,
295
+ CB_LOG_DEBUG("DNS TCP write operation has been aborted, address=\"{}\", ec={}",
296
+ logger::system_data(self->nameserver_address()),
238
297
  ec2.message());
239
298
  self->deadline_.cancel();
240
- return self->handler_({ ec2 });
299
+ return self->complete({ ec2 });
241
300
  }
242
301
  asio::async_read(
243
302
  self->tcp_,
@@ -257,14 +316,13 @@ private:
257
316
  return;
258
317
  }
259
318
  CB_LOG_DEBUG(
260
- "DNS TCP buf size read operation has been aborted, address=\"{}:{}\", ec={}",
261
- self->address_.to_string(),
262
- self->port_,
319
+ "DNS TCP buf size read operation has been aborted, address=\"{}\", ec={}",
320
+ logger::system_data(self->nameserver_address()),
263
321
  ec3.message());
264
322
  self->deadline_.cancel();
265
- return self->handler_({ ec3 });
323
+ return self->complete({ ec3 });
266
324
  }
267
- self->recv_buf_size_ = utils::byte_swap(self->recv_buf_size_);
325
+ self->recv_buf_size_ = utils::network_to_host(self->recv_buf_size_);
268
326
  self->recv_buf_.resize(self->recv_buf_size_);
269
327
  CB_LOG_DEBUG("DNS TCP schedule read of {} bytes", self->recv_buf_size_);
270
328
  asio::async_read(
@@ -285,15 +343,19 @@ private:
285
343
  }
286
344
  self->deadline_.cancel();
287
345
  if (ec4) {
288
- CB_LOG_DEBUG(
289
- "DNS TCP read operation has been aborted, address=\"{}:{}\", ec={}",
290
- self->address_.to_string(),
291
- self->port_,
292
- ec4.message());
293
- return self->handler_({ ec4 });
346
+ CB_LOG_DEBUG("DNS TCP read operation has been aborted, address=\"{}\", ec={}",
347
+ logger::system_data(self->nameserver_address()),
348
+ ec4.message());
349
+ return self->complete({ ec4 });
294
350
  }
295
351
  self->recv_buf_.resize(bytes_transferred4);
296
352
  const dns_message message = dns_codec::decode(self->recv_buf_);
353
+ if (message.header.flags.rcode != response_code::no_error) {
354
+ CB_LOG_WARNING("DNS TCP response for \"{}\" returned non-zero RCODE: {} ({})",
355
+ logger::system_data(self->nameserver_address()),
356
+ static_cast<int>(message.header.flags.rcode),
357
+ rcode_description(message.header.flags.rcode));
358
+ }
297
359
  dns_srv_response resp{ ec4 };
298
360
  resp.targets.reserve(message.answers.size());
299
361
  for (const auto& answer : message.answers) {
@@ -301,13 +363,14 @@ private:
301
363
  utils::join_strings(answer.target.labels, "."), answer.port });
302
364
  }
303
365
  CB_LOG_DEBUG("DNS TCP returned {} records", resp.targets.size());
304
- return self->handler_(std::move(resp));
366
+ return self->complete(std::move(resp));
305
367
  });
306
368
  });
307
369
  });
308
370
  });
309
371
  }
310
372
 
373
+ asio::io_context& ctx_;
311
374
  asio::steady_timer deadline_;
312
375
  asio::steady_timer udp_deadline_;
313
376
  asio::ip::udp::socket udp_;
@@ -323,6 +386,7 @@ private:
323
386
  std::vector<std::uint8_t> recv_buf_{};
324
387
 
325
388
  std::atomic_bool retrying_with_tcp_{ false };
389
+ std::int64_t owner_pid_{ utils::current_process_id() };
326
390
  };
327
391
 
328
392
  void
@@ -332,12 +396,25 @@ dns_client::query_srv(const std::string& name,
332
396
  utils::movable_function<void(dns_srv_response&&)>&& handler)
333
397
  {
334
398
  if (config.nameserver().empty()) {
399
+ CB_LOG_WARNING(
400
+ "DNS-SRV query for \"{}/{}\" skipped: no nameserver configured (DNS-SRV bootstrap is "
401
+ "disabled). Check that /etc/resolv.conf contains a valid IP address on a \"nameserver\" "
402
+ "line, or set the nameserver explicitly in the cluster options.",
403
+ logger::system_data(name),
404
+ service);
335
405
  return handler({ {} });
336
406
  }
337
407
 
338
408
  std::error_code ec;
339
409
  auto address = asio::ip::make_address(config.nameserver(), ec);
340
410
  if (ec) {
411
+ CB_LOG_WARNING(
412
+ "DNS-SRV query for \"{}/{}\" skipped: configured nameserver \"{}\" could not be parsed as "
413
+ "an IP address ({}). Only numeric IP addresses are supported as nameserver values.",
414
+ logger::system_data(name),
415
+ service,
416
+ logger::system_data(config.nameserver()),
417
+ ec.message());
341
418
  return handler({ ec });
342
419
  }
343
420
  auto cmd = std::make_shared<dns_srv_command>(