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
@@ -161,15 +161,30 @@ cb_HdrHistogramC_get_percentiles_and_reset(VALUE self, VALUE percentiles)
161
161
  cb_hdr_histogram_data* hdr_histogram;
162
162
  TypedData_Get_Struct(self, cb_hdr_histogram_data, &cb_hdr_histogram_type, hdr_histogram);
163
163
 
164
+ // Validate before any C++ object is live and before taking the lock: a raise longjmps past
165
+ // destructors, so a raise under the lock would leave the mutex locked.
166
+ const long percentiles_len = RARRAY_LEN(percentiles);
167
+ for (long i = 0; i < percentiles_len; ++i) {
168
+ VALUE entry = rb_ary_entry(percentiles, i);
169
+ if (!RB_FLOAT_TYPE_P(entry) && !RB_INTEGER_TYPE_P(entry)) {
170
+ rb_raise(rb_eTypeError,
171
+ "wrong argument type %" PRIsVALUE " (expected Float or Integer)",
172
+ rb_obj_class(entry));
173
+ }
174
+ }
175
+ std::vector<double> percentile_args{};
176
+ percentile_args.reserve(static_cast<std::size_t>(percentiles_len));
177
+ for (long i = 0; i < percentiles_len; ++i) {
178
+ percentile_args.push_back(NUM2DBL(rb_ary_entry(percentiles, i)));
179
+ }
180
+
164
181
  std::vector<std::int64_t> percentile_values{};
182
+ percentile_values.reserve(percentile_args.size());
165
183
  std::int64_t total_count;
166
184
  {
167
185
  const std::unique_lock lock(hdr_histogram->mutex);
168
186
  total_count = hdr_histogram->histogram->total_count;
169
- for (std::size_t i = 0; i < static_cast<std::size_t>(RARRAY_LEN(percentiles)); ++i) {
170
- VALUE entry = rb_ary_entry(percentiles, static_cast<long>(i));
171
- Check_Type(entry, T_FLOAT);
172
- double perc = NUM2DBL(entry);
187
+ for (const double perc : percentile_args) {
173
188
  std::int64_t value_at_perc = hdr_value_at_percentile(hdr_histogram->histogram, perc);
174
189
  percentile_values.push_back(value_at_perc);
175
190
  }
data/ext/rcb_logger.cxx CHANGED
@@ -28,10 +28,14 @@
28
28
  #include <spdlog/sinks/base_sink.h>
29
29
  #include <spdlog/spdlog.h>
30
30
 
31
+ #include <algorithm>
32
+ #include <deque>
33
+ #include <iterator>
31
34
  #include <memory>
32
- #include <queue>
35
+ #include <mutex>
33
36
 
34
37
  #include <ruby.h>
38
+ #include <ruby/vm.h>
35
39
 
36
40
  #include "rcb_logger.hxx"
37
41
  #include "rcb_utils.hxx"
@@ -49,14 +53,59 @@ public:
49
53
  {
50
54
  }
51
55
 
52
- void flush_deferred_messages()
56
+ // Returns the rb_protect state of the failed write, or zero. The caller jumps with it once its
57
+ // own C++ objects are destroyed.
58
+ int flush_deferred_messages()
53
59
  {
54
- std::lock_guard<Mutex> lock(spdlog::sinks::base_sink<Mutex>::mutex_);
55
- auto messages_ = std::move(deferred_messages_);
56
- while (!messages_.empty()) {
57
- write_message(messages_.front());
58
- messages_.pop();
60
+ int state = 0;
61
+ {
62
+ // The mutex is held only to move messages in and out of the queue. A longjmp out of the
63
+ // Ruby logger would leave it locked, and a logger that releases the GVL would deadlock with
64
+ // a flush that holds the GVL and waits for the mutex.
65
+ std::deque<log_message_for_ruby> messages{};
66
+ {
67
+ std::lock_guard<Mutex> lock(spdlog::sinks::base_sink<Mutex>::mutex_);
68
+ std::swap(messages, deferred_messages_);
69
+ }
70
+ // install_logger_shim may replace @__logger_shim while the logger runs, which drops the last
71
+ // reference to it.
72
+ VALUE logger = ruby_logger_;
73
+ while (state == 0 && !messages.empty()) {
74
+ state = write_message(logger, messages.front());
75
+ // A message is retried once. An interrupt may land before its callback wrote it, and a
76
+ // logger that fails on it every time must not stop the queue.
77
+ if (state == 0 || messages.front().failed) {
78
+ messages.pop_front();
79
+ } else {
80
+ messages.front().failed = true;
81
+ }
82
+ }
83
+ RB_GC_GUARD(logger);
84
+ // After a failed write the unwritten messages go back to the front of the queue, ahead of
85
+ // those logged meanwhile. The caller's jump is not
86
+ // delayed by writing them, and rb_errinfo() is left as the failing callback set it.
87
+ if (!messages.empty()) {
88
+ std::lock_guard<Mutex> lock(spdlog::sinks::base_sink<Mutex>::mutex_);
89
+ std::move(
90
+ deferred_messages_.begin(), deferred_messages_.end(), std::back_inserter(messages));
91
+ std::swap(messages, deferred_messages_);
92
+ }
59
93
  }
94
+ return state;
95
+ }
96
+
97
+ // Moves the messages queued in other to the front of this sink's queue.
98
+ void take_deferred_messages(ruby_logger_sink& other)
99
+ {
100
+ if (&other == this) {
101
+ return;
102
+ }
103
+ std::scoped_lock lock(this->mutex_, other.mutex_);
104
+ std::move(deferred_messages_.begin(),
105
+ deferred_messages_.end(),
106
+ std::back_inserter(other.deferred_messages_));
107
+ std::swap(other.deferred_messages_, deferred_messages_);
108
+ other.deferred_messages_.clear();
60
109
  }
61
110
 
62
111
  static VALUE map_log_level(spdlog::level::level_enum level)
@@ -91,11 +140,12 @@ protected:
91
140
  const char* filename{ "<file>" };
92
141
  int line{};
93
142
  const char* funcname{ "<func>" };
143
+ bool failed{ false };
94
144
  };
95
145
 
96
146
  void sink_it_(const spdlog::details::log_msg& msg) override
97
147
  {
98
- deferred_messages_.emplace(log_message_for_ruby{
148
+ deferred_messages_.emplace_back(log_message_for_ruby{
99
149
  msg.level,
100
150
  msg.time,
101
151
  msg.thread_id,
@@ -137,7 +187,8 @@ private:
137
187
  function_name = cb_str_new(msg.funcname);
138
188
  }
139
189
  auto seconds = std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch());
140
- auto nanoseconds = msg.time.time_since_epoch() - seconds;
190
+ auto nanoseconds =
191
+ std::chrono::duration_cast<std::chrono::nanoseconds>(msg.time.time_since_epoch() - seconds);
141
192
  return rb_funcall(args->logger,
142
193
  rb_intern("log"),
143
194
  8,
@@ -151,18 +202,27 @@ private:
151
202
  function_name);
152
203
  }
153
204
 
154
- void write_message(const log_message_for_ruby& msg)
205
+ static VALUE invoke_log_rescue_standard_error(VALUE arg)
155
206
  {
156
- if (NIL_P(ruby_logger_)) {
157
- return;
207
+ return rb_rescue(invoke_log, arg, nullptr, Qnil);
208
+ }
209
+
210
+ // Returns the rb_protect state, non-zero when the logger call ended by a jump other than a
211
+ // rescued StandardError: another exception, a throw, or a thread kill.
212
+ static int write_message(VALUE logger, const log_message_for_ruby& msg)
213
+ {
214
+ if (NIL_P(logger)) {
215
+ return 0;
158
216
  }
159
- argument_pack args{ ruby_logger_, msg };
217
+ argument_pack args{ logger, msg };
218
+ int state = 0;
160
219
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
161
- rb_rescue(invoke_log, reinterpret_cast<VALUE>(&args), nullptr, Qnil);
220
+ rb_protect(invoke_log_rescue_standard_error, reinterpret_cast<VALUE>(&args), &state);
221
+ return state;
162
222
  }
163
223
 
164
224
  VALUE ruby_logger_{ Qnil };
165
- std::queue<log_message_for_ruby> deferred_messages_{};
225
+ std::deque<log_message_for_ruby> deferred_messages_{};
166
226
  };
167
227
 
168
228
  using ruby_logger_sink_ptr = std::shared_ptr<ruby_logger_sink<std::mutex>>;
@@ -226,15 +286,27 @@ cb_Backend_get_log_level(VALUE /* self */)
226
286
  return Qnil;
227
287
  }
228
288
 
229
- VALUE
230
- cb_Backend_install_logger_shim(VALUE self, VALUE logger, VALUE log_level)
289
+ // Returns the rb_protect state of a failed flush, or zero. When sink is no longer the current
290
+ // one, its unwritten messages move to the front of the current sink's queue, because a replaced
291
+ // sink is never flushed again.
292
+ int
293
+ flush_sink(const ruby_logger_sink_ptr& sink)
231
294
  {
232
- core::logger::reset();
295
+ int state = sink->flush_deferred_messages();
296
+ if (state != 0 && cb_global_sink && cb_global_sink != sink) {
297
+ cb_global_sink->take_deferred_messages(*sink);
298
+ }
299
+ return state;
300
+ }
301
+
302
+ void
303
+ install_logger_sink(VALUE self, VALUE logger, VALUE log_level)
304
+ {
305
+ cb_global_sink.reset();
233
306
  rb_iv_set(self, "@__logger_shim", logger);
234
307
  if (NIL_P(logger)) {
235
- return Qnil;
308
+ return;
236
309
  }
237
- Check_Type(log_level, T_SYMBOL);
238
310
  core::logger::level level{ core::logger::level::off };
239
311
  if (ID type = rb_sym2id(log_level); type == rb_intern("trace")) {
240
312
  level = core::logger::level::trace;
@@ -249,8 +321,8 @@ cb_Backend_install_logger_shim(VALUE self, VALUE logger, VALUE log_level)
249
321
  } else if (type == rb_intern("critical")) {
250
322
  level = core::logger::level::critical;
251
323
  } else {
252
- rb_iv_set(self, "__logger_shim", Qnil);
253
- return Qnil;
324
+ rb_iv_set(self, "@__logger_shim", Qnil);
325
+ return;
254
326
  }
255
327
 
256
328
  auto sink = std::make_shared<ruby_logger_sink<std::mutex>>(logger);
@@ -260,6 +332,26 @@ cb_Backend_install_logger_shim(VALUE self, VALUE logger, VALUE log_level)
260
332
  configuration.sink = sink;
261
333
  core::logger::create_file_logger(configuration);
262
334
  cb_global_sink = sink;
335
+ }
336
+
337
+ VALUE
338
+ cb_Backend_install_logger_shim(VALUE self, VALUE logger, VALUE log_level)
339
+ {
340
+ if (!NIL_P(logger)) {
341
+ cb_check_type(log_level, T_SYMBOL);
342
+ }
343
+ VALUE old_logger = rb_iv_get(self, "@__logger_shim");
344
+ // The new sink is installed before the old one is flushed, so a failed flush leaves the new
345
+ // logger in place and hands it the old sink's unwritten messages. Without a new logger they
346
+ // have no destination.
347
+ auto old_sink = cb_global_sink;
348
+ core::logger::reset();
349
+ install_logger_sink(self, logger, log_level);
350
+ int state = old_sink ? flush_sink(old_sink) : 0;
351
+ RB_GC_GUARD(old_logger);
352
+ if (state != 0) {
353
+ throw ruby_jump(state);
354
+ }
263
355
  return Qnil;
264
356
  }
265
357
 
@@ -298,22 +390,105 @@ init_logger()
298
390
  }
299
391
  }
300
392
 
393
+ int
394
+ try_flush_logger()
395
+ {
396
+ if (auto sink = cb_global_sink; sink) {
397
+ return flush_sink(sink);
398
+ }
399
+ core::logger::flush();
400
+ return 0;
401
+ }
402
+
301
403
  void
302
404
  flush_logger()
303
405
  {
304
- if (cb_global_sink) {
305
- cb_global_sink->flush_deferred_messages();
306
- } else {
307
- core::logger::flush();
406
+ if (int state = try_flush_logger(); state != 0) {
407
+ rb_jump_tag(state);
308
408
  }
309
409
  }
310
410
 
411
+ int
412
+ try_flush_logger_interruptible()
413
+ {
414
+ int state = 0;
415
+ rb_protect(
416
+ [](VALUE) noexcept -> VALUE {
417
+ VALUE mask = rb_hash_new();
418
+ rb_hash_aset(mask, rb_cObject, rb_id2sym(rb_intern("immediate")));
419
+ return rb_block_call(
420
+ rb_cThread,
421
+ rb_intern("handle_interrupt"),
422
+ 1,
423
+ &mask,
424
+ [](VALUE, VALUE, int, const VALUE*, VALUE) noexcept -> VALUE {
425
+ flush_logger();
426
+ return Qnil;
427
+ },
428
+ Qnil);
429
+ },
430
+ Qnil,
431
+ &state);
432
+ return state;
433
+ }
434
+
435
+ namespace
436
+ {
437
+ // Joins the async logger thread while it is still running. VM at-exit hooks
438
+ // run after every end proc and finalizer, so what those log, such as a cluster
439
+ // closed in its finalizer, still reaches the built-in sinks. Left to the spdlog
440
+ // destructors, the join happens after ExitProcess has killed the thread. That
441
+ // never returns under winpthreads, and the Ruby process hangs on Windows.
442
+ void
443
+ shutdown_logger(ruby_vm_t* /* vm */)
444
+ {
445
+ core::logger::shutdown();
446
+ }
447
+ } // namespace
448
+
311
449
  void
312
450
  init_logger_methods(VALUE cBackend)
313
451
  {
452
+ // A failed flush leaves messages queued. At exit, flush again after a failure that is an
453
+ // ordinary exception; a message is consumed after its second failure, so the loop ends. A signal,
454
+ // exit, throw or thread kill stops the loop and is re-raised for the end-proc runner.
455
+ rb_set_end_proc(
456
+ [](VALUE) {
457
+ // The swallowed exception must not replace the $! the exit started with, which the logger
458
+ // sees on the next attempt. rb_set_errinfo accepts only nil or an Exception.
459
+ VALUE exit_error = rb_errinfo();
460
+ bool restorable = NIL_P(exit_error) || (RB_TYPE_P(exit_error, T_OBJECT) &&
461
+ RTEST(rb_obj_is_kind_of(exit_error, rb_eException)));
462
+ int state = 0;
463
+ for (;;) {
464
+ rb_protect(
465
+ [](VALUE) -> VALUE {
466
+ flush_logger();
467
+ return Qnil;
468
+ },
469
+ Qnil,
470
+ &state);
471
+ if (state != 0) {
472
+ VALUE error = rb_errinfo();
473
+ if (!RB_TYPE_P(error, T_OBJECT) || RTEST(rb_obj_is_kind_of(error, rb_eSignal)) ||
474
+ RTEST(rb_obj_is_kind_of(error, rb_eSystemExit))) {
475
+ rb_jump_tag(state);
476
+ }
477
+ }
478
+ if (restorable) {
479
+ rb_set_errinfo(exit_error);
480
+ }
481
+ if (state == 0) {
482
+ return;
483
+ }
484
+ }
485
+ },
486
+ Qnil);
487
+ ruby_vm_at_exit(shutdown_logger);
314
488
  rb_define_singleton_method(cBackend, "set_log_level", cb_Backend_set_log_level, 1);
315
489
  rb_define_singleton_method(cBackend, "get_log_level", cb_Backend_get_log_level, 0);
316
- rb_define_singleton_method(cBackend, "install_logger_shim", cb_Backend_install_logger_shim, 2);
490
+ rb_define_singleton_method(
491
+ cBackend, "install_logger_shim", cb_method<cb_Backend_install_logger_shim>::invoke, 2);
317
492
  rb_define_singleton_method(cBackend,
318
493
  "enable_protocol_logger_to_save_network_traffic_to_file",
319
494
  cb_Backend_enable_protocol_logger_to_save_network_traffic_to_file,
data/ext/rcb_logger.hxx CHANGED
@@ -31,6 +31,18 @@ init_logger();
31
31
  void
32
32
  flush_logger();
33
33
 
34
+ // Flushes like flush_logger, but returns the rb_protect state of a failed logger call instead of
35
+ // re-raising it. The caller jumps with it once its own C++ objects are destroyed.
36
+ int
37
+ try_flush_logger();
38
+
39
+ // Like try_flush_logger, but accepts asynchronous interrupts while the logger runs, even inside
40
+ // Thread.handle_interrupt(Object => :never). An interrupt delivered there ends the flush as a
41
+ // failed logger call and is returned as its rb_protect state. One deferred before the call is
42
+ // delivered at the first logger call, and stays deferred when nothing is queued.
43
+ int
44
+ try_flush_logger_interruptible();
45
+
34
46
  void
35
47
  init_logger_methods(VALUE cBackend);
36
48
 
data/ext/rcb_multi.cxx CHANGED
@@ -51,7 +51,7 @@ cb_extract_array_of_ids(std::vector<core::document_id>& ids, VALUE arg)
51
51
  if (TYPE(arg) != T_ARRAY) {
52
52
  throw ruby_exception(
53
53
  rb_eArgError,
54
- rb_sprintf("Type of IDs argument must be an Array, but given %+" PRIsVALUE, arg));
54
+ cb_sprintf("Type of IDs argument must be an Array, but given %+" PRIsVALUE, arg));
55
55
  }
56
56
  auto num_of_ids = static_cast<std::size_t>(RARRAY_LEN(arg));
57
57
  if (num_of_ids < 1) {
@@ -62,30 +62,30 @@ cb_extract_array_of_ids(std::vector<core::document_id>& ids, VALUE arg)
62
62
  VALUE entry = rb_ary_entry(arg, static_cast<long>(i));
63
63
  if (TYPE(entry) != T_ARRAY || RARRAY_LEN(entry) != 4) {
64
64
  throw ruby_exception(rb_eArgError,
65
- rb_sprintf("ID tuple must be represented as an Array[bucket, scope, "
65
+ cb_sprintf("ID tuple must be represented as an Array[bucket, scope, "
66
66
  "collection, id], but given %+" PRIsVALUE,
67
67
  entry));
68
68
  }
69
69
  VALUE bucket = rb_ary_entry(entry, 0);
70
70
  if (TYPE(bucket) != T_STRING) {
71
71
  throw ruby_exception(rb_eArgError,
72
- rb_sprintf("Bucket must be a String, but given %+" PRIsVALUE, bucket));
72
+ cb_sprintf("Bucket must be a String, but given %+" PRIsVALUE, bucket));
73
73
  }
74
74
  VALUE scope = rb_ary_entry(entry, 1);
75
75
  if (TYPE(scope) != T_STRING) {
76
76
  throw ruby_exception(rb_eArgError,
77
- rb_sprintf("Scope must be a String, but given %+" PRIsVALUE, scope));
77
+ cb_sprintf("Scope must be a String, but given %+" PRIsVALUE, scope));
78
78
  }
79
79
  VALUE collection = rb_ary_entry(entry, 2);
80
80
  if (TYPE(collection) != T_STRING) {
81
81
  throw ruby_exception(
82
82
  rb_eArgError,
83
- rb_sprintf("Collection must be a String, but given %+" PRIsVALUE, collection));
83
+ cb_sprintf("Collection must be a String, but given %+" PRIsVALUE, collection));
84
84
  }
85
85
  VALUE id = rb_ary_entry(entry, 3);
86
86
  if (TYPE(id) != T_STRING) {
87
87
  throw ruby_exception(rb_eArgError,
88
- rb_sprintf("ID must be a String, but given %+" PRIsVALUE, id));
88
+ cb_sprintf("ID must be a String, but given %+" PRIsVALUE, id));
89
89
  }
90
90
  ids.emplace_back(
91
91
  cb_string_new(bucket), cb_string_new(scope), cb_string_new(collection), cb_string_new(id));
@@ -107,7 +107,7 @@ cb_extract_array_of_id_content(
107
107
  if (TYPE(tuples) != T_ARRAY) {
108
108
  throw ruby_exception(
109
109
  rb_eArgError,
110
- rb_sprintf("Type of ID/content tuples must be an Array, but given %+" PRIsVALUE, tuples));
110
+ cb_sprintf("Type of ID/content tuples must be an Array, but given %+" PRIsVALUE, tuples));
111
111
  }
112
112
 
113
113
  auto num_of_tuples = static_cast<std::size_t>(RARRAY_LEN(tuples));
@@ -119,24 +119,24 @@ cb_extract_array_of_id_content(
119
119
  VALUE entry = rb_ary_entry(tuples, static_cast<long>(i));
120
120
  if (TYPE(entry) != T_ARRAY || RARRAY_LEN(entry) != 3) {
121
121
  throw ruby_exception(rb_eArgError,
122
- rb_sprintf("ID/content tuple must be represented as an Array[id, "
122
+ cb_sprintf("ID/content tuple must be represented as an Array[id, "
123
123
  "content, flags], but given %+" PRIsVALUE,
124
124
  entry));
125
125
  }
126
126
  VALUE id = rb_ary_entry(entry, 0);
127
127
  if (TYPE(id) != T_STRING) {
128
128
  throw ruby_exception(rb_eArgError,
129
- rb_sprintf("ID must be a String, but given %+" PRIsVALUE, id));
129
+ cb_sprintf("ID must be a String, but given %+" PRIsVALUE, id));
130
130
  }
131
131
  VALUE content = rb_ary_entry(entry, 1);
132
132
  if (TYPE(content) != T_STRING) {
133
133
  throw ruby_exception(rb_eArgError,
134
- rb_sprintf("Content must be a String, but given %+" PRIsVALUE, content));
134
+ cb_sprintf("Content must be a String, but given %+" PRIsVALUE, content));
135
135
  }
136
136
  VALUE flags = rb_ary_entry(entry, 2);
137
137
  if (TYPE(flags) != T_FIXNUM) {
138
138
  throw ruby_exception(rb_eArgError,
139
- rb_sprintf("Flags must be an Integer, but given %+" PRIsVALUE, flags));
139
+ cb_sprintf("Flags must be an Integer, but given %+" PRIsVALUE, flags));
140
140
  }
141
141
  id_content.emplace_back(core::document_id{ cb_string_new(bucket_name),
142
142
  cb_string_new(scope_name),
@@ -144,7 +144,7 @@ cb_extract_array_of_id_content(
144
144
  cb_string_new(id) },
145
145
  couchbase::codec::encoded_value{
146
146
  cb_binary_new(content),
147
- FIX2UINT(flags),
147
+ cb_num2uint(flags),
148
148
  });
149
149
  }
150
150
  }
@@ -163,11 +163,11 @@ cb_extract_array_of_id_cas(std::vector<std::pair<core::document_id, couchbase::c
163
163
  if (TYPE(tuples) != T_ARRAY) {
164
164
  throw ruby_exception(
165
165
  rb_eArgError,
166
- rb_sprintf("Type of ID/CAS tuples must be an Array, but given %+" PRIsVALUE, tuples));
166
+ cb_sprintf("Type of ID/CAS tuples must be an Array, but given %+" PRIsVALUE, tuples));
167
167
  }
168
168
  auto num_of_tuples = static_cast<std::size_t>(RARRAY_LEN(tuples));
169
169
  if (num_of_tuples < 1) {
170
- rb_raise(rb_eArgError, "Array of ID/CAS tuples must not be empty");
170
+ throw ruby_exception(rb_eArgError, "Array of ID/CAS tuples must not be empty");
171
171
  }
172
172
  id_cas.reserve(num_of_tuples);
173
173
  for (std::size_t i = 0; i < num_of_tuples; ++i) {
@@ -175,14 +175,14 @@ cb_extract_array_of_id_cas(std::vector<std::pair<core::document_id, couchbase::c
175
175
  if (TYPE(entry) != T_ARRAY || RARRAY_LEN(entry) != 2) {
176
176
  throw ruby_exception(
177
177
  rb_eArgError,
178
- rb_sprintf(
178
+ cb_sprintf(
179
179
  "ID/content tuple must be represented as an Array[id, CAS], but given %+" PRIsVALUE,
180
180
  entry));
181
181
  }
182
182
  VALUE id = rb_ary_entry(entry, 0);
183
183
  if (TYPE(id) != T_STRING) {
184
184
  throw ruby_exception(rb_eArgError,
185
- rb_sprintf("ID must be a String, but given %+" PRIsVALUE, id));
185
+ cb_sprintf("ID must be a String, but given %+" PRIsVALUE, id));
186
186
  }
187
187
  couchbase::cas cas_val{};
188
188
  if (VALUE cas = rb_ary_entry(entry, 1); !NIL_P(cas)) {
@@ -205,6 +205,7 @@ cb_Backend_document_get_multi(VALUE self, VALUE keys, VALUE options)
205
205
  try {
206
206
  std::chrono::milliseconds timeout{ 0 };
207
207
  cb_extract_timeout(timeout, options);
208
+ auto retry_strategy = cb_extract_retry_strategy(options);
208
209
 
209
210
  std::vector<core::document_id> ids{};
210
211
  cb_extract_array_of_ids(ids, keys);
@@ -214,7 +215,7 @@ cb_Backend_document_get_multi(VALUE self, VALUE keys, VALUE options)
214
215
  futures.reserve(num_of_ids);
215
216
 
216
217
  for (auto& id : ids) {
217
- core::operations::get_request req{ std::move(id) };
218
+ core::operations::get_request req{ std::move(id), {}, {}, {}, { retry_strategy } };
218
219
  if (timeout.count() > 0) {
219
220
  req.timeout = timeout;
220
221
  }
@@ -243,10 +244,8 @@ cb_Backend_document_get_multi(VALUE self, VALUE keys, VALUE options)
243
244
 
244
245
  return res;
245
246
  } catch (const std::system_error& se) {
246
- rb_exc_raise(cb_map_error_code(
247
+ throw ruby_exception(cb_map_error_code(
247
248
  se.code(), fmt::format("failed to perform {}: {}", __func__, se.what()), false));
248
- } catch (const ruby_exception& e) {
249
- rb_exc_raise(e.exception_object());
250
249
  }
251
250
  return Qnil;
252
251
  }
@@ -261,16 +260,17 @@ cb_Backend_document_upsert_multi(VALUE self,
261
260
  {
262
261
  auto cluster = cb_backend_to_core_api_cluster(self);
263
262
 
264
- Check_Type(bucket, T_STRING);
265
- Check_Type(scope, T_STRING);
266
- Check_Type(collection, T_STRING);
263
+ cb_check_type(bucket, T_STRING);
264
+ cb_check_type(scope, T_STRING);
265
+ cb_check_type(collection, T_STRING);
267
266
  if (!NIL_P(options)) {
268
- Check_Type(options, T_HASH);
267
+ cb_check_type(options, T_HASH);
269
268
  }
270
269
 
271
270
  try {
272
271
  std::vector<std::pair<core::document_id, couchbase::codec::encoded_value>> tuples{};
273
272
  cb_extract_array_of_id_content(tuples, bucket, scope, collection, id_content);
273
+ auto retry_strategy = cb_extract_retry_strategy(options);
274
274
 
275
275
  auto num_of_tuples = tuples.size();
276
276
  std::vector<std::pair<std::string, std::future<core::operations::upsert_response>>> futures;
@@ -278,8 +278,8 @@ cb_Backend_document_upsert_multi(VALUE self,
278
278
 
279
279
  for (auto& [id, content] : tuples) {
280
280
  core::operations::upsert_request req{
281
- id,
282
- std::move(content.data),
281
+ id, std::move(content.data), {}, {}, 0, 0, couchbase::durability_level::none,
282
+ {}, { retry_strategy },
283
283
  };
284
284
  req.flags = content.flags;
285
285
  cb_extract_timeout(req, options);
@@ -326,10 +326,8 @@ cb_Backend_document_upsert_multi(VALUE self,
326
326
  return res;
327
327
 
328
328
  } catch (const std::system_error& se) {
329
- rb_exc_raise(cb_map_error_code(
329
+ throw ruby_exception(cb_map_error_code(
330
330
  se.code(), fmt::format("failed to perform {}: {}", __func__, se.what()), false));
331
- } catch (const ruby_exception& e) {
332
- rb_exc_raise(e.exception_object());
333
331
  }
334
332
  return Qnil;
335
333
  }
@@ -344,16 +342,17 @@ cb_Backend_document_remove_multi(VALUE self,
344
342
  {
345
343
  auto cluster = cb_backend_to_core_api_cluster(self);
346
344
 
347
- Check_Type(bucket, T_STRING);
348
- Check_Type(scope, T_STRING);
349
- Check_Type(collection, T_STRING);
345
+ cb_check_type(bucket, T_STRING);
346
+ cb_check_type(scope, T_STRING);
347
+ cb_check_type(collection, T_STRING);
350
348
  if (!NIL_P(options)) {
351
- Check_Type(options, T_HASH);
349
+ cb_check_type(options, T_HASH);
352
350
  }
353
351
 
354
352
  try {
355
353
  std::vector<std::pair<core::document_id, couchbase::cas>> tuples{};
356
354
  cb_extract_array_of_id_cas(tuples, bucket, scope, collection, id_cas);
355
+ auto retry_strategy = cb_extract_retry_strategy(options);
357
356
 
358
357
  auto num_of_tuples = tuples.size();
359
358
  std::vector<std::pair<std::string, std::future<core::operations::remove_response>>> futures;
@@ -361,7 +360,7 @@ cb_Backend_document_remove_multi(VALUE self,
361
360
 
362
361
  for (const auto& [id, cas] : tuples) {
363
362
  core::operations::remove_request req{
364
- id,
363
+ id, {}, {}, {}, couchbase::durability_level::none, {}, { retry_strategy },
365
364
  };
366
365
  cb_extract_timeout(req, options);
367
366
  cb_extract_durability_level(req, options);
@@ -406,10 +405,8 @@ cb_Backend_document_remove_multi(VALUE self,
406
405
  return res;
407
406
 
408
407
  } catch (const std::system_error& se) {
409
- rb_exc_raise(cb_map_error_code(
408
+ throw ruby_exception(cb_map_error_code(
410
409
  se.code(), fmt::format("failed to perform {}: {}", __func__, se.what()), false));
411
- } catch (const ruby_exception& e) {
412
- rb_exc_raise(e.exception_object());
413
410
  }
414
411
  return Qnil;
415
412
  }
@@ -419,8 +416,11 @@ cb_Backend_document_remove_multi(VALUE self,
419
416
  void
420
417
  init_multi(VALUE cBackend)
421
418
  {
422
- rb_define_method(cBackend, "document_get_multi", cb_Backend_document_get_multi, 2);
423
- rb_define_method(cBackend, "document_remove_multi", cb_Backend_document_remove_multi, 5);
424
- rb_define_method(cBackend, "document_upsert_multi", cb_Backend_document_upsert_multi, 5);
419
+ rb_define_method(
420
+ cBackend, "document_get_multi", cb_method<cb_Backend_document_get_multi>::invoke, 2);
421
+ rb_define_method(
422
+ cBackend, "document_remove_multi", cb_method<cb_Backend_document_remove_multi>::invoke, 5);
423
+ rb_define_method(
424
+ cBackend, "document_upsert_multi", cb_method<cb_Backend_document_upsert_multi>::invoke, 5);
425
425
  }
426
426
  } // namespace couchbase::ruby
@@ -89,11 +89,13 @@ cb_add_core_spans(VALUE observability_handler,
89
89
  rb_ary_push(spans, core_span_to_rb_hash(child));
90
90
  }
91
91
 
92
- static ID add_backend_spans_func = rb_intern("add_spans_from_backend");
93
- rb_funcall(observability_handler, add_backend_spans_func, 1, spans);
92
+ cb_protect([observability_handler, spans, retry_attempts] {
93
+ static ID add_backend_spans_func = rb_intern("add_spans_from_backend");
94
+ rb_funcall(observability_handler, add_backend_spans_func, 1, spans);
94
95
 
95
- static ID add_retries_func = rb_intern("add_retries");
96
- rb_funcall(observability_handler, add_retries_func, 1, ULONG2NUM(retry_attempts));
96
+ static ID add_retries_func = rb_intern("add_retries");
97
+ rb_funcall(observability_handler, add_retries_func, 1, ULONG2NUM(retry_attempts));
98
+ });
97
99
  }
98
100
 
99
101
  namespace
@@ -127,6 +129,6 @@ cb_Backend_cluster_labels(VALUE self)
127
129
  void
128
130
  init_observability(VALUE cBackend)
129
131
  {
130
- rb_define_method(cBackend, "cluster_labels", cb_Backend_cluster_labels, 0);
132
+ rb_define_method(cBackend, "cluster_labels", cb_method<cb_Backend_cluster_labels>::invoke, 0);
131
133
  }
132
134
  } // namespace couchbase::ruby