couchbase 3.8.1 → 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 (475) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  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 +214 -155
  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 +325 -25
  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 +66 -0
  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 +316 -317
  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 +275 -14
  131. data/ext/couchbase/core/io/streams.hxx +53 -0
  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/bucket_create.cxx +37 -38
  183. data/ext/couchbase/core/operations/management/bucket_update.cxx +26 -28
  184. data/ext/couchbase/core/operations/management/change_password.cxx +2 -0
  185. data/ext/couchbase/core/operations/management/collection_drop.cxx +5 -1
  186. data/ext/couchbase/core/operations/management/error_utils.cxx +59 -5
  187. data/ext/couchbase/core/operations/management/error_utils.hxx +29 -1
  188. data/ext/couchbase/core/operations/management/eventing_deploy_function.cxx +9 -3
  189. data/ext/couchbase/core/operations/management/eventing_drop_function.cxx +8 -3
  190. data/ext/couchbase/core/operations/management/eventing_get_all_functions.cxx +4 -2
  191. data/ext/couchbase/core/operations/management/eventing_get_function.cxx +5 -3
  192. data/ext/couchbase/core/operations/management/eventing_get_status.cxx +4 -2
  193. data/ext/couchbase/core/operations/management/eventing_pause_function.cxx +9 -3
  194. data/ext/couchbase/core/operations/management/eventing_resume_function.cxx +9 -3
  195. data/ext/couchbase/core/operations/management/eventing_undeploy_function.cxx +9 -3
  196. data/ext/couchbase/core/operations/management/eventing_upsert_function.cxx +9 -3
  197. data/ext/couchbase/core/operations/management/group_drop.cxx +3 -1
  198. data/ext/couchbase/core/operations/management/group_get.cxx +3 -1
  199. data/ext/couchbase/core/operations/management/group_upsert.cxx +11 -8
  200. data/ext/couchbase/core/operations/management/query_index_build.cxx +5 -2
  201. data/ext/couchbase/core/operations/management/query_index_create.cxx +9 -3
  202. data/ext/couchbase/core/operations/management/query_index_drop.cxx +11 -4
  203. data/ext/couchbase/core/operations/management/query_index_get_all.cxx +1 -1
  204. data/ext/couchbase/core/operations/management/query_index_get_all_deferred.cxx +1 -1
  205. data/ext/couchbase/core/operations/management/scope_drop.cxx +5 -1
  206. data/ext/couchbase/core/operations/management/scope_get_all.cxx +6 -1
  207. data/ext/couchbase/core/operations/management/search_get_stats.cxx +10 -0
  208. data/ext/couchbase/core/operations/management/search_index_analyze_document.cxx +3 -2
  209. data/ext/couchbase/core/operations/management/search_index_control_ingest.cxx +4 -3
  210. data/ext/couchbase/core/operations/management/search_index_control_plan_freeze.cxx +4 -3
  211. data/ext/couchbase/core/operations/management/search_index_control_query.cxx +4 -3
  212. data/ext/couchbase/core/operations/management/search_index_drop.cxx +2 -2
  213. data/ext/couchbase/core/operations/management/search_index_get.cxx +2 -2
  214. data/ext/couchbase/core/operations/management/search_index_get_documents_count.cxx +3 -2
  215. data/ext/couchbase/core/operations/management/search_index_get_stats.cxx +3 -1
  216. data/ext/couchbase/core/operations/management/search_index_upsert.cxx +2 -2
  217. data/ext/couchbase/core/operations/management/user_drop.cxx +3 -1
  218. data/ext/couchbase/core/operations/management/user_get.cxx +3 -1
  219. data/ext/couchbase/core/operations/management/user_upsert.cxx +12 -9
  220. data/ext/couchbase/core/operations/management/view_index_drop.cxx +1 -1
  221. data/ext/couchbase/core/operations/management/view_index_get.cxx +1 -1
  222. data/ext/couchbase/core/operations/management/view_index_upsert.cxx +1 -1
  223. data/ext/couchbase/core/operations/operation_traits.hxx +18 -0
  224. data/ext/couchbase/core/operations/query_response_parsing.cxx +211 -0
  225. data/ext/couchbase/core/operations/query_response_parsing.hxx +60 -0
  226. data/ext/couchbase/core/operations_fwd.hxx +3 -3
  227. data/ext/couchbase/core/origin.cxx +54 -26
  228. data/ext/couchbase/core/origin.hxx +12 -0
  229. data/ext/couchbase/core/orphan_reporter.cxx +2 -1
  230. data/ext/couchbase/core/platform/random.cc +7 -7
  231. data/ext/couchbase/core/protocol/client_request.cxx +2 -2
  232. data/ext/couchbase/core/protocol/client_request.hxx +38 -21
  233. data/ext/couchbase/core/protocol/client_response.cxx +11 -3
  234. data/ext/couchbase/core/protocol/client_response.hxx +27 -6
  235. data/ext/couchbase/core/protocol/cmd_append.cxx +2 -2
  236. data/ext/couchbase/core/protocol/cmd_cluster_map_change_notification.cxx +2 -2
  237. data/ext/couchbase/core/protocol/cmd_decrement.cxx +6 -6
  238. data/ext/couchbase/core/protocol/cmd_get.cxx +1 -1
  239. data/ext/couchbase/core/protocol/cmd_get_and_lock.cxx +2 -2
  240. data/ext/couchbase/core/protocol/cmd_get_and_touch.cxx +2 -2
  241. data/ext/couchbase/core/protocol/cmd_get_cluster_config.cxx +6 -4
  242. data/ext/couchbase/core/protocol/cmd_get_collection_id.cxx +2 -2
  243. data/ext/couchbase/core/protocol/cmd_get_error_map.cxx +1 -1
  244. data/ext/couchbase/core/protocol/cmd_get_meta.cxx +4 -4
  245. data/ext/couchbase/core/protocol/cmd_get_replica.cxx +1 -1
  246. data/ext/couchbase/core/protocol/cmd_hello.cxx +1 -1
  247. data/ext/couchbase/core/protocol/cmd_increment.cxx +6 -6
  248. data/ext/couchbase/core/protocol/cmd_insert.cxx +4 -4
  249. data/ext/couchbase/core/protocol/cmd_lookup_in.cxx +4 -3
  250. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +4 -3
  251. data/ext/couchbase/core/protocol/cmd_mutate_in.cxx +10 -9
  252. data/ext/couchbase/core/protocol/cmd_mutate_in.hxx +10 -10
  253. data/ext/couchbase/core/protocol/cmd_observe_seqno.cxx +7 -7
  254. data/ext/couchbase/core/protocol/cmd_prepend.cxx +2 -2
  255. data/ext/couchbase/core/protocol/cmd_remove.cxx +2 -2
  256. data/ext/couchbase/core/protocol/cmd_replace.cxx +4 -4
  257. data/ext/couchbase/core/protocol/cmd_touch.cxx +1 -1
  258. data/ext/couchbase/core/protocol/cmd_upsert.cxx +4 -4
  259. data/ext/couchbase/core/protocol/datatype.hxx +118 -1
  260. data/ext/couchbase/core/protocol/frame_info_utils.cxx +1 -1
  261. data/ext/couchbase/core/protocol/magic.hxx +4 -0
  262. data/ext/couchbase/core/protocol/magic_fmt.hxx +3 -0
  263. data/ext/couchbase/core/protocol/server_request.hxx +3 -1
  264. data/ext/couchbase/core/protocol/status.cxx +1 -1
  265. data/ext/couchbase/core/protostellar/analytics_converter.hxx +171 -0
  266. data/ext/couchbase/core/protostellar/bucket_admin_converter.hxx +301 -0
  267. data/ext/couchbase/core/protostellar/collection_admin_converter.hxx +92 -0
  268. data/ext/couchbase/core/protostellar/component.cxx +2445 -0
  269. data/ext/couchbase/core/protostellar/component.hxx +391 -0
  270. data/ext/couchbase/core/{columnar/bootstrap_notification_subscriber.hxx → protostellar/compression_settings.hxx} +11 -10
  271. data/ext/couchbase/core/protostellar/credentials.cxx +233 -0
  272. data/ext/couchbase/core/protostellar/credentials.hxx +88 -0
  273. data/ext/couchbase/core/protostellar/dispatcher.cxx +57 -0
  274. data/ext/couchbase/core/protostellar/dispatcher.hxx +482 -0
  275. data/ext/couchbase/core/protostellar/error_utils.cxx +293 -0
  276. data/ext/couchbase/core/protostellar/error_utils.hxx +97 -0
  277. data/ext/couchbase/core/protostellar/json_payload.hxx +49 -0
  278. data/ext/couchbase/core/protostellar/kv_converter.cxx +70 -0
  279. data/ext/couchbase/core/protostellar/kv_converter.hxx +600 -0
  280. data/ext/couchbase/core/protostellar/query_converter.hxx +265 -0
  281. data/ext/couchbase/core/protostellar/query_index_admin_converter.hxx +291 -0
  282. data/ext/couchbase/core/protostellar/query_proto.hxx +73 -0
  283. data/ext/couchbase/core/protostellar/search_converter.hxx +244 -0
  284. data/ext/couchbase/core/protostellar/search_index_admin_converter.hxx +156 -0
  285. data/ext/couchbase/core/protostellar/view_converter.hxx +158 -0
  286. data/ext/couchbase/core/query_stream.cxx +447 -0
  287. data/ext/couchbase/core/query_stream.hxx +141 -0
  288. data/ext/couchbase/core/query_stream_component.cxx +243 -0
  289. data/ext/couchbase/core/query_stream_component.hxx +68 -0
  290. data/ext/couchbase/core/range_scan_load_balancer.cxx +15 -8
  291. data/ext/couchbase/core/range_scan_orchestrator.cxx +15 -3
  292. data/ext/couchbase/core/response_handler.hxx +41 -4
  293. data/ext/couchbase/core/row_streamer.cxx +332 -46
  294. data/ext/couchbase/core/row_streamer.hxx +31 -1
  295. data/ext/couchbase/core/row_streamer_options.hxx +55 -0
  296. data/ext/couchbase/core/sasl/client.h +3 -0
  297. data/ext/couchbase/core/search_scoring.hxx +66 -0
  298. data/ext/couchbase/core/stream_error_details.hxx +69 -0
  299. data/ext/couchbase/core/timeout_defaults.hxx +0 -1
  300. data/ext/couchbase/core/topology/capabilities.hxx +6 -0
  301. data/ext/couchbase/core/topology/capabilities_fmt.hxx +3 -0
  302. data/ext/couchbase/core/topology/configuration.cxx +21 -6
  303. data/ext/couchbase/core/topology/configuration.hxx +29 -8
  304. data/ext/couchbase/core/topology/configuration_json.hxx +3 -0
  305. data/ext/couchbase/core/topology/error_map_json.hxx +3 -1
  306. data/ext/couchbase/core/tracing/threshold_logging_tracer.cxx +70 -4
  307. data/ext/couchbase/core/tracing/threshold_logging_tracer.hxx +10 -0
  308. data/ext/couchbase/core/transactions/active_transaction_record.cxx +0 -34
  309. data/ext/couchbase/core/transactions/attempt_context_impl.cxx +362 -241
  310. data/ext/couchbase/core/transactions/attempt_context_impl.hxx +24 -3
  311. data/ext/couchbase/core/transactions/error_list.hxx +2 -2
  312. data/ext/couchbase/core/transactions/get_multi_fetch.hxx +160 -0
  313. data/ext/couchbase/core/transactions/get_multi_orchestrator.cxx +326 -125
  314. data/ext/couchbase/core/transactions/get_multi_transaction_id.hxx +62 -0
  315. data/ext/couchbase/core/transactions/internal/atr_entry.hxx +22 -2
  316. data/ext/couchbase/core/transactions/internal/transaction_context.hxx +2 -2
  317. data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +23 -1
  318. data/ext/couchbase/core/transactions/internal/utils.hxx +50 -14
  319. data/ext/couchbase/core/transactions/staged_mutation.cxx +23 -18
  320. data/ext/couchbase/core/transactions/staged_mutation.hxx +15 -4
  321. data/ext/couchbase/core/transactions/transaction_context.cxx +55 -40
  322. data/ext/couchbase/core/transactions/transaction_get_result.hxx +16 -0
  323. data/ext/couchbase/core/transactions/transactions.cxx +3 -55
  324. data/ext/couchbase/core/transactions/transactions_cleanup.cxx +194 -66
  325. data/ext/couchbase/core/transactions/transactions_config.cxx +0 -11
  326. data/ext/couchbase/core/transactions/utils.cxx +10 -0
  327. data/ext/couchbase/core/transactions/waitable_op_list.hxx +15 -4
  328. data/ext/couchbase/core/transactions.hxx +9 -9
  329. data/ext/couchbase/core/utils/byteswap.hxx +166 -9
  330. data/ext/couchbase/core/utils/connection_string.cxx +50 -85
  331. data/ext/couchbase/core/utils/connection_string.hxx +15 -0
  332. data/ext/couchbase/core/{columnar/backoff_calculator.hxx → utils/deadline.hxx} +13 -13
  333. data/ext/couchbase/core/utils/duration_parser.cxx +1 -1
  334. data/ext/couchbase/core/utils/json_streaming_lexer.cxx +48 -5
  335. data/ext/couchbase/core/utils/json_streaming_lexer.hxx +4 -1
  336. data/ext/couchbase/core/utils/movable_function.hxx +301 -52
  337. data/ext/couchbase/core/utils/name_codec.hxx +154 -11
  338. data/ext/couchbase/core/{columnar/retry_info.hxx → utils/process_id.hxx} +18 -13
  339. data/ext/couchbase/core/utils/url_codec.cxx +12 -51
  340. data/ext/couchbase/core/websocket_codec.cxx +3 -29
  341. data/ext/couchbase/couchbase/analytics_options.hxx +21 -10
  342. data/ext/couchbase/couchbase/analytics_row.hxx +105 -0
  343. data/ext/couchbase/couchbase/analytics_stream_result.hxx +266 -0
  344. data/ext/couchbase/couchbase/boolean_query.hxx +3 -3
  345. data/ext/couchbase/couchbase/bucket.hxx +57 -0
  346. data/ext/couchbase/couchbase/cluster.hxx +216 -5
  347. data/ext/couchbase/{core/columnar/security_options.hxx → couchbase/cluster_state.hxx} +18 -14
  348. data/ext/couchbase/couchbase/codec/lenient_json_transcoder.hxx +69 -0
  349. data/ext/couchbase/couchbase/collection.hxx +234 -14
  350. data/ext/couchbase/couchbase/common_durability_options.hxx +18 -3
  351. data/ext/couchbase/couchbase/conjunction_query.hxx +1 -1
  352. data/ext/couchbase/couchbase/create_dataset_analytics_options.hxx +6 -1
  353. data/ext/couchbase/couchbase/date_range_query.hxx +2 -2
  354. data/ext/couchbase/couchbase/decrement_options.hxx +1 -1
  355. data/ext/couchbase/couchbase/disjunction_query.hxx +1 -1
  356. data/ext/couchbase/couchbase/error.hxx +32 -0
  357. data/ext/couchbase/couchbase/error_codes.hxx +32 -0
  358. data/ext/couchbase/couchbase/geo_bounding_box_query.hxx +1 -1
  359. data/ext/couchbase/couchbase/geo_distance_query.hxx +1 -1
  360. data/ext/couchbase/couchbase/geo_polygon_query.hxx +1 -1
  361. data/ext/couchbase/couchbase/get_replica_options.hxx +68 -0
  362. data/ext/couchbase/couchbase/get_replica_result.hxx +5 -3
  363. data/ext/couchbase/couchbase/get_replica_strategy.hxx +161 -0
  364. data/ext/couchbase/couchbase/get_result.hxx +1 -0
  365. data/ext/couchbase/couchbase/increment_options.hxx +1 -1
  366. data/ext/couchbase/couchbase/insert_options.hxx +1 -1
  367. data/ext/couchbase/couchbase/match_phrase_query.hxx +1 -1
  368. data/ext/couchbase/couchbase/match_query.hxx +1 -1
  369. data/ext/couchbase/couchbase/mutate_in_options.hxx +3 -3
  370. data/ext/couchbase/couchbase/network_options.hxx +1 -1
  371. data/ext/couchbase/couchbase/node_id.hxx +42 -14
  372. data/ext/couchbase/couchbase/node_id_for_options.hxx +7 -1
  373. data/ext/couchbase/couchbase/node_ids_options.hxx +8 -2
  374. data/ext/couchbase/couchbase/numeric_range_query.hxx +1 -1
  375. data/ext/couchbase/couchbase/phrase_query.hxx +1 -1
  376. data/ext/couchbase/couchbase/prefix_query.hxx +1 -1
  377. data/ext/couchbase/couchbase/query_options.hxx +23 -12
  378. data/ext/couchbase/couchbase/query_row.hxx +111 -0
  379. data/ext/couchbase/couchbase/query_stream_result.hxx +302 -0
  380. data/ext/couchbase/couchbase/query_string_query.hxx +3 -3
  381. data/ext/couchbase/couchbase/regexp_query.hxx +1 -1
  382. data/ext/couchbase/couchbase/replace_options.hxx +3 -3
  383. data/ext/couchbase/couchbase/result.hxx +14 -3
  384. data/ext/couchbase/couchbase/scan_result_item.hxx +1 -0
  385. data/ext/couchbase/couchbase/scope.hxx +85 -2
  386. data/ext/couchbase/couchbase/search_options.hxx +58 -8
  387. data/ext/couchbase/couchbase/search_scoring.hxx +94 -0
  388. data/ext/couchbase/couchbase/search_scoring_none.hxx +51 -0
  389. data/ext/couchbase/couchbase/search_scoring_reciprocal_rank_fusion.hxx +88 -0
  390. data/ext/couchbase/couchbase/search_scoring_relative_score_fusion.hxx +70 -0
  391. data/ext/couchbase/couchbase/term_range_query.hxx +1 -1
  392. data/ext/couchbase/couchbase/tracing/request_span.hxx +46 -0
  393. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +23 -21
  394. data/ext/couchbase/couchbase/transactions/transaction_get_multi_result.hxx +24 -19
  395. data/ext/couchbase/couchbase/transactions/transaction_get_result.hxx +17 -3
  396. data/ext/couchbase/couchbase/transactions/transactions_config.hxx +4 -2
  397. data/ext/couchbase/couchbase/transactions.hxx +27 -18
  398. data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
  399. data/ext/couchbase/couchbase/vector_query.hxx +1 -1
  400. data/ext/couchbase/couchbase/wait_until_ready_options.hxx +111 -0
  401. data/ext/couchbase/couchbase/wildcard_query.hxx +1 -1
  402. data/ext/extconf.rb +15 -10
  403. data/ext/rcb_analytics.cxx +82 -102
  404. data/ext/rcb_backend.cxx +42 -36
  405. data/ext/rcb_backend.hxx +2 -0
  406. data/ext/rcb_buckets.cxx +47 -59
  407. data/ext/rcb_collections.cxx +42 -52
  408. data/ext/rcb_crud.cxx +345 -224
  409. data/ext/rcb_diagnostics.cxx +117 -27
  410. data/ext/rcb_exceptions.cxx +152 -99
  411. data/ext/rcb_exceptions.hxx +25 -0
  412. data/ext/rcb_extras.cxx +18 -19
  413. data/ext/rcb_hdr_histogram.cxx +19 -4
  414. data/ext/rcb_logger.cxx +202 -27
  415. data/ext/rcb_logger.hxx +12 -0
  416. data/ext/rcb_multi.cxx +40 -40
  417. data/ext/rcb_observability.cxx +7 -5
  418. data/ext/rcb_query.cxx +99 -142
  419. data/ext/rcb_range_scan.cxx +41 -35
  420. data/ext/rcb_search.cxx +98 -99
  421. data/ext/rcb_users.cxx +36 -56
  422. data/ext/rcb_utils.cxx +100 -37
  423. data/ext/rcb_utils.hxx +287 -33
  424. data/ext/rcb_version.cxx +1 -1
  425. data/ext/rcb_views.cxx +55 -51
  426. data/lib/active_support/cache/couchbase_store.rb +77 -12
  427. data/lib/couchbase/bucket.rb +20 -0
  428. data/lib/couchbase/cluster.rb +33 -14
  429. data/lib/couchbase/collection.rb +36 -1
  430. data/lib/couchbase/errors.rb +18 -0
  431. data/lib/couchbase/fork_hooks.rb +16 -7
  432. data/lib/couchbase/get_replica_strategy.rb +89 -0
  433. data/lib/couchbase/logger.rb +1 -1
  434. data/lib/couchbase/management/analytics_index_manager.rb +15 -15
  435. data/lib/couchbase/management/bucket_manager.rb +6 -6
  436. data/lib/couchbase/management/collection_manager.rb +6 -6
  437. data/lib/couchbase/management/query_index_manager.rb +7 -7
  438. data/lib/couchbase/management/search_index_manager.rb +13 -13
  439. data/lib/couchbase/management/user_manager.rb +10 -10
  440. data/lib/couchbase/management/view_index_manager.rb +5 -5
  441. data/lib/couchbase/options.rb +181 -44
  442. data/lib/couchbase/protostellar/bucket.rb +4 -0
  443. data/lib/couchbase/protostellar/cluster.rb +4 -0
  444. data/lib/couchbase/protostellar/collection.rb +4 -0
  445. data/lib/couchbase/protostellar/error_handling.rb +2 -1
  446. data/lib/couchbase/protostellar/request.rb +1 -0
  447. data/lib/couchbase/protostellar/request_generator/kv.rb +16 -2
  448. data/lib/couchbase/protostellar/request_generator/search.rb +17 -1
  449. data/lib/couchbase/protostellar/retry/orchestrator.rb +3 -2
  450. data/lib/couchbase/protostellar/retry/strategies/fail_fast.rb +33 -0
  451. data/lib/couchbase/protostellar/retry/strategies.rb +1 -0
  452. data/lib/couchbase/search_options.rb +95 -6
  453. data/lib/couchbase/version.rb +1 -1
  454. metadata +92 -33
  455. data/ext/0001-fix-build-for-mingw-w64-ucrt-x86_64-toolchain.patch +0 -40
  456. data/ext/couchbase/core/columnar/agent.hxx +0 -71
  457. data/ext/couchbase/core/columnar/agent_config.hxx +0 -59
  458. data/ext/couchbase/core/columnar/background_bootstrap_listener.hxx +0 -42
  459. data/ext/couchbase/core/columnar/backoff_calculator.cxx +0 -62
  460. data/ext/couchbase/core/columnar/columnar_agent.cxx +0 -147
  461. data/ext/couchbase/core/columnar/columnar_agent_config.cxx +0 -48
  462. data/ext/couchbase/core/columnar/columnar_error.cxx +0 -51
  463. data/ext/couchbase/core/columnar/database_management_options.hxx +0 -56
  464. data/ext/couchbase/core/columnar/error.hxx +0 -51
  465. data/ext/couchbase/core/columnar/error_codes.cxx +0 -107
  466. data/ext/couchbase/core/columnar/error_codes.hxx +0 -75
  467. data/ext/couchbase/core/columnar/management_component.cxx +0 -300
  468. data/ext/couchbase/core/columnar/management_component.hxx +0 -56
  469. data/ext/couchbase/core/columnar/query_component.cxx +0 -523
  470. data/ext/couchbase/core/columnar/query_component.hxx +0 -57
  471. data/ext/couchbase/core/columnar/query_options.hxx +0 -59
  472. data/ext/couchbase/core/columnar/query_result.cxx +0 -130
  473. data/ext/couchbase/core/columnar/query_result.hxx +0 -78
  474. data/ext/couchbase/core/io/config_tracker.cxx +0 -687
  475. data/ext/couchbase/core/io/config_tracker.hxx +0 -123
@@ -0,0 +1,122 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2024. Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #pragma once
19
+
20
+ #include <array>
21
+ #include <cstddef>
22
+ #include <utility>
23
+ #include <vector>
24
+
25
+ namespace couchbase::core::io
26
+ {
27
+ /**
28
+ * A tiny free-list of byte buffers used to recycle mcbp response body buffers.
29
+ *
30
+ * A KV response's body is allocated when the frame is parsed and freed a moment later when the
31
+ * decoded response is destroyed. Recycling those buffers keeps their capacity warm, so a subsequent
32
+ * response of similar size reuses the storage instead of allocating (and, once the value crosses
33
+ * the allocator's mmap threshold, mmap/munmap-ing) a fresh block every operation.
34
+ *
35
+ * NOT thread-safe by design, and it performs no synchronization. It is intended to be used as a
36
+ * thread-local pool (see tls_response_body_pool()): acquire and release always happen on the
37
+ * calling thread's own pool, so a buffer acquired on one IO thread but released on another (for
38
+ * example when a replica response is handed to a command strand running on a different IO thread)
39
+ * is simply not recycled back to the acquiring thread — which is safe, because each thread only
40
+ * ever touches its own pool. Recycling is therefore best-effort.
41
+ *
42
+ * Two bounds keep memory in check: at most `max_buffers` buffers are retained, and buffers larger
43
+ * than `max_buffer_bytes` are never pooled (they are freed normally), so a single very large value
44
+ * cannot pin megabytes per thread.
45
+ *
46
+ * The free list is a fixed-size array of (initially empty) buffers, so constructing the pool
47
+ * allocates nothing. That matters because the pool is a lazily-initialized thread_local (see
48
+ * tls_response_body_pool()): the first use on a given thread may be a release() from a response
49
+ * destructor, and that first use triggers the thread_local's construction. Backing the free list
50
+ * with a std::vector would make that construction allocate (and, on failure, throw) inside the
51
+ * noexcept destructor path, where a throw calls std::terminate. A fixed array cannot.
52
+ */
53
+ class mcbp_buffer_pool
54
+ {
55
+ public:
56
+ using buffer = std::vector<std::byte>;
57
+
58
+ static constexpr std::size_t max_buffers = 16;
59
+ static constexpr std::size_t default_max_buffer_bytes = std::size_t{ 1024 } * 1024;
60
+
61
+ explicit mcbp_buffer_pool(std::size_t retained_buffers = max_buffers,
62
+ std::size_t max_buffer_bytes = default_max_buffer_bytes)
63
+ : retained_buffers_{ retained_buffers < max_buffers ? retained_buffers : max_buffers }
64
+ , max_buffer_bytes_{ max_buffer_bytes }
65
+ {
66
+ // No allocation here by design (see the class comment): the free list is a fixed-size array of
67
+ // empty buffers, so neither this constructor nor the lazy thread_local init it drives can
68
+ // throw.
69
+ }
70
+
71
+ /**
72
+ * Hand out a recycled buffer (cleared, capacity retained) or an empty one if the pool is empty.
73
+ */
74
+ auto acquire() -> buffer
75
+ {
76
+ if (count_ == 0) {
77
+ return {};
78
+ }
79
+ // Move the buffer out, leaving an empty slot (the moved-from vector holds no storage).
80
+ return std::move(free_[--count_]);
81
+ }
82
+
83
+ /**
84
+ * Return a buffer for reuse. Empty buffers (nothing to recycle) and buffers above the size cap
85
+ * are dropped, as is any buffer beyond the retained-count bound.
86
+ */
87
+ void release(buffer&& buf) noexcept
88
+ {
89
+ if (buf.capacity() == 0 || buf.capacity() > max_buffer_bytes_ || count_ >= retained_buffers_) {
90
+ return;
91
+ }
92
+ buf.clear();
93
+ // Slots at index >= count_ are always empty, so this move-assignment frees nothing and
94
+ // allocates nothing; release() therefore never touches the allocator, as its noexcept contract
95
+ // requires.
96
+ free_[count_++] = std::move(buf);
97
+ }
98
+
99
+ [[nodiscard]] auto size() const -> std::size_t
100
+ {
101
+ return count_;
102
+ }
103
+
104
+ private:
105
+ std::array<buffer, max_buffers> free_{};
106
+ std::size_t count_{ 0 };
107
+ std::size_t retained_buffers_;
108
+ std::size_t max_buffer_bytes_;
109
+ };
110
+
111
+ /**
112
+ * The calling thread's response-body buffer pool. Shared by every mcbp connection served on that IO
113
+ * thread; because it is thread-local, acquire and release are always confined to a single thread
114
+ * and need no locking (see mcbp_buffer_pool).
115
+ */
116
+ inline auto
117
+ tls_response_body_pool() -> mcbp_buffer_pool&
118
+ {
119
+ static thread_local mcbp_buffer_pool pool;
120
+ return pool;
121
+ }
122
+ } // namespace couchbase::core::io
@@ -18,11 +18,11 @@
18
18
  #pragma once
19
19
 
20
20
  #include "core/app_telemetry_meter.hxx"
21
- #include "core/document_id_fmt.hxx"
21
+ #include "core/document_id_redaction.hxx"
22
22
  #include "core/error_context/key_value_error_map_info.hxx"
23
+ #include "core/logger/redaction.hxx"
23
24
  #include "core/metrics/meter_wrapper.hxx"
24
25
  #include "core/operations/operation_traits.hxx"
25
- #include "core/platform/uuid.h"
26
26
  #include "core/protocol/client_request.hxx"
27
27
  #include "core/protocol/client_response.hxx"
28
28
  #include "core/protocol/cmd_get_collection_id.hxx"
@@ -30,17 +30,23 @@
30
30
  #include "core/utils/movable_function.hxx"
31
31
  #include "couchbase/metrics/meter.hxx"
32
32
  #include "couchbase/tracing/request_tracer.hxx"
33
+ #include "mcbp_command_id.hxx"
33
34
  #include "mcbp_session.hxx"
34
35
  #include "mcbp_traits.hxx"
35
36
  #include "retry_orchestrator.hxx"
36
37
 
38
+ #include <couchbase/build_config.hxx>
37
39
  #include <couchbase/durability_level.hxx>
38
40
  #include <couchbase/error_codes.hxx>
39
41
 
42
+ #include <asio/dispatch.hpp>
43
+ #include <asio/io_context.hpp>
40
44
  #include <asio/steady_timer.hpp>
45
+ #include <asio/strand.hpp>
41
46
  #include <spdlog/fmt/bundled/chrono.h>
42
47
 
43
48
  #include <functional>
49
+ #include <type_traits>
44
50
  #include <utility>
45
51
 
46
52
  namespace couchbase::core::operations
@@ -51,12 +57,22 @@ using mcbp_command_handler =
51
57
 
52
58
  template<typename Manager, typename Request>
53
59
  struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager, Request>> {
54
- static constexpr std::chrono::milliseconds durability_timeout_floor{ 1'500 };
55
-
56
60
  using encoded_request_type = typename Request::encoded_request_type;
57
61
  using encoded_response_type = typename Request::encoded_response_type;
58
62
  asio::steady_timer deadline;
59
63
  asio::steady_timer retry_backoff;
64
+ // Only cancellable operations (replica fan-out) need a strand: their dispatch
65
+ // path can run on the caller thread while a sibling's completion cancels them
66
+ // from an IO thread, so all access to session_/opaque_/handler_ is confined to
67
+ // it. Non-cancellable operations get an empty placeholder instead, so they pay
68
+ // neither the strand_impl allocation nor any access on the hot path; the strand
69
+ // is referenced only inside `if constexpr (is_cancellable_operation_v<...>)`.
70
+ struct unused_strand {
71
+ };
72
+ using command_strand = std::conditional_t<is_cancellable_operation_v<Request>,
73
+ asio::strand<asio::io_context::executor_type>,
74
+ unused_strand>;
75
+ command_strand strand_;
60
76
  Request request;
61
77
  encoded_request_type encoded;
62
78
  std::optional<std::uint32_t> opaque_{};
@@ -64,9 +80,8 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
64
80
  mcbp_command_handler handler_{};
65
81
  std::shared_ptr<Manager> manager_{};
66
82
  std::chrono::milliseconds timeout_{};
67
- std::string id_{ fmt::format("{:02x}/{}",
68
- static_cast<std::uint8_t>(encoded_request_type::body_type::opcode),
69
- uuid::to_string(uuid::random())) };
83
+ // Built lazily by id(); empty until first requested. See id().
84
+ mutable std::string id_{};
70
85
  #ifdef COUCHBASE_CXX_CLIENT_CREATE_OPERATION_SPAN_IN_CORE
71
86
  std::shared_ptr<couchbase::tracing::request_span> span_{ nullptr };
72
87
  #endif
@@ -76,29 +91,48 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
76
91
  std::chrono::time_point<std::chrono::steady_clock> started_at_{};
77
92
  std::vector<std::chrono::microseconds> server_durations_{};
78
93
 
94
+ static auto make_command_strand(asio::io_context& ctx) -> command_strand
95
+ {
96
+ if constexpr (is_cancellable_operation_v<Request>) {
97
+ return command_strand{ ctx.get_executor() };
98
+ } else {
99
+ return command_strand{};
100
+ }
101
+ }
102
+
79
103
  mcbp_command(asio::io_context& ctx,
80
104
  std::shared_ptr<Manager> manager,
81
105
  Request req,
82
106
  std::chrono::milliseconds default_timeout)
83
107
  : deadline(ctx)
84
108
  , retry_backoff(ctx)
109
+ , strand_(make_command_strand(ctx))
85
110
  , request(req)
86
111
  , manager_(manager)
87
112
  , timeout_(request.timeout.value_or(default_timeout))
88
113
  , parent_span_(request.parent_span)
89
114
  {
90
- if constexpr (io::mcbp_traits::supports_durability_v<Request>) {
91
- if (request.durability_level != durability_level::none &&
92
- timeout_ < durability_timeout_floor) {
93
- CB_LOG_DEBUG(
94
- R"(Timeout is too low for operation with durability, increasing to sensible value. timeout={}ms, floor={}ms, id="{}")",
95
- request.id,
96
- timeout_.count(),
97
- durability_timeout_floor.count(),
98
- id_);
99
- timeout_ = durability_timeout_floor;
100
- }
115
+ }
116
+
117
+ // The diagnostic identifier ("<opcode>/<uuid>") is used only in log lines,
118
+ // retry traces, and the returned key_value error context — never on the wire.
119
+ // Build it lazily on first use and cache it, so it is materialized at most once
120
+ // per command instead of eagerly in every constructor, and repeated readers -- a
121
+ // retry log line and then the completion's error context -- share the one cached
122
+ // value. The completion path builds the error context, so a completed operation
123
+ // still materializes it once; the laziness only avoids the cost for commands that
124
+ // never reach a point that needs it. The cache is logically const (it does not
125
+ // change the command's observable value), hence the mutable member and const
126
+ // accessor. Access while the command is in flight
127
+ // is serialized -- non-cancellable operations run on their session's single io
128
+ // thread, and cancellable (replica) operations confine access to the command
129
+ // strand (see strand_) -- so the one-time memoizing write needs no synchronization.
130
+ [[nodiscard]] auto id() const -> const std::string&
131
+ {
132
+ if (id_.empty()) {
133
+ id_ = make_command_id(encoded_request_type::body_type::opcode);
101
134
  }
135
+ return id_;
102
136
  }
103
137
 
104
138
  void start(mcbp_command_handler&& handler)
@@ -133,6 +167,16 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
133
167
 
134
168
  void cancel(retry_reason reason, bool is_timeout = true)
135
169
  {
170
+ if constexpr (is_cancellable_operation_v<Request>) {
171
+ // Confine cancellation to the command strand so it cannot race the
172
+ // dispatch path (map_and_send/send_to/send) of a replica sibling that is
173
+ // still being set up on another thread.
174
+ if (!strand_.running_in_this_thread()) {
175
+ return asio::dispatch(strand_, [self = this->shared_from_this(), reason, is_timeout]() {
176
+ self->cancel(reason, is_timeout);
177
+ });
178
+ }
179
+ }
136
180
  if (opaque_ && session_) {
137
181
  if (session_->cancel(opaque_.value(), asio::error::operation_aborted, reason)) {
138
182
  handler_ = nullptr;
@@ -149,6 +193,36 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
149
193
  }
150
194
  }
151
195
 
196
+ // Re-dispatch a session completion handler onto the command strand for
197
+ // cancellable operations, so the response/timeout path (which mcbp_session
198
+ // invokes on its own IO thread) cannot run concurrently with a strand-confined
199
+ // cancel(). A no-op passthrough for non-cancellable operations.
200
+ template<typename Handler>
201
+ auto on_strand(Handler&& handler)
202
+ {
203
+ if constexpr (is_cancellable_operation_v<Request>) {
204
+ return [self = this->shared_from_this(), handler = std::forward<Handler>(handler)](
205
+ std::error_code ec,
206
+ retry_reason reason,
207
+ io::mcbp_message&& msg,
208
+ std::optional<key_value_error_map_info> error_info) mutable {
209
+ if (self->strand_.running_in_this_thread()) {
210
+ return handler(ec, reason, std::move(msg), std::move(error_info));
211
+ }
212
+ asio::dispatch(self->strand_,
213
+ [handler = std::move(handler),
214
+ ec,
215
+ reason,
216
+ msg = std::move(msg),
217
+ error_info = std::move(error_info)]() mutable {
218
+ handler(ec, reason, std::move(msg), std::move(error_info));
219
+ });
220
+ };
221
+ } else {
222
+ return std::forward<Handler>(handler);
223
+ }
224
+ }
225
+
152
226
  void invoke_handler(std::error_code ec, std::optional<io::mcbp_message>&& msg = {})
153
227
  {
154
228
  retry_backoff.cancel();
@@ -162,18 +236,19 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
162
236
  }
163
237
  #endif
164
238
  if (handler) {
165
- const auto& node_uuid = session_ ? session_->node_uuid() : "";
166
239
  auto telemetry_recorder =
167
- manager_->app_telemetry_meter()->value_recorder(node_uuid, manager_->name());
240
+ session_
241
+ ? session_->app_telemetry_recorder(*manager_->app_telemetry_meter(), manager_->name())
242
+ : manager_->app_telemetry_meter()->value_recorder("", manager_->name());
168
243
  telemetry_recorder->update_counter(app_telemetry_counter::kv_r_total);
169
244
  if (ec == errc::common::unambiguous_timeout || ec == errc::common::ambiguous_timeout) {
170
245
  telemetry_recorder->update_counter(app_telemetry_counter::kv_r_timedout);
171
246
  auto time_left = deadline.expiry() - std::chrono::steady_clock::now();
172
247
  CB_LOG_TRACE(R"([{}] timeout operation id="{}", {}, key="{}", partition={}, time_left={})",
173
248
  session_ ? session_->log_prefix() : manager_->log_prefix(),
174
- id_,
249
+ id(),
175
250
  encoded_request_type::body_type::opcode,
176
- request.id,
251
+ logger::document(request.id),
177
252
  request.partition,
178
253
  time_left);
179
254
  } else if (ec == errc::common::request_canceled) {
@@ -185,6 +260,15 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
185
260
 
186
261
  void request_collection_id()
187
262
  {
263
+ if constexpr (is_cancellable_operation_v<Request>) {
264
+ // Reached from the unknown-collection retry timer on an arbitrary IO
265
+ // thread; hop onto the strand before touching session_/opaque_.
266
+ if (!strand_.running_in_this_thread()) {
267
+ return asio::dispatch(strand_, [self = this->shared_from_this()]() {
268
+ self->request_collection_id();
269
+ });
270
+ }
271
+ }
188
272
  if (session_->is_stopped()) {
189
273
  return manager_->map_and_send(this->shared_from_this());
190
274
  }
@@ -194,11 +278,11 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
194
278
  session_->write_and_subscribe(
195
279
  req.opaque(),
196
280
  req.data(session_->supports_feature(protocol::hello_feature::snappy)),
197
- [self = this->shared_from_this()](
198
- std::error_code ec,
199
- retry_reason /* reason */,
200
- io::mcbp_message&& msg,
201
- std::optional<key_value_error_map_info> /* error_info */) mutable {
281
+ on_strand([self = this->shared_from_this()](
282
+ std::error_code ec,
283
+ retry_reason /* reason */,
284
+ io::mcbp_message&& msg,
285
+ std::optional<key_value_error_map_info> /* error_info */) mutable {
202
286
  if (ec == asio::error::operation_aborted) {
203
287
  return self->invoke_handler(errc::common::ambiguous_timeout);
204
288
  }
@@ -216,7 +300,7 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
216
300
  resp.body().collection_uid());
217
301
  self->request.id.collection_uid(resp.body().collection_uid());
218
302
  return self->send();
219
- });
303
+ }));
220
304
  }
221
305
 
222
306
  void handle_unknown_collection()
@@ -225,9 +309,9 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
225
309
  auto time_left = deadline.expiry() - std::chrono::steady_clock::now();
226
310
  CB_LOG_DEBUG(R"({} unknown collection response for "{}", time_left={}ms, id="{}")",
227
311
  session_->log_prefix(),
228
- request.id,
312
+ logger::document(request.id),
229
313
  std::chrono::duration_cast<std::chrono::milliseconds>(time_left).count(),
230
- id_);
314
+ id());
231
315
  request.retries.add_reason(retry_reason::key_value_collection_outdated);
232
316
  if (time_left < backoff) {
233
317
  return invoke_handler(make_error_code(request.retries.idempotent()
@@ -256,9 +340,9 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
256
340
  CB_LOG_DEBUG(
257
341
  R"({} no cache entry for collection, resolve collection id for "{}", timeout={}ms, id="{}")",
258
342
  session_->log_prefix(),
259
- request.id,
343
+ logger::document(request.id),
260
344
  timeout_.count(),
261
- id_);
345
+ id());
262
346
  return request_collection_id();
263
347
  }
264
348
  } else {
@@ -283,13 +367,13 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
283
367
  session_->write_and_subscribe(
284
368
  request.opaque,
285
369
  encoded.data(session_->supports_feature(protocol::hello_feature::snappy)),
286
- [self = this->shared_from_this(),
287
- start = std::chrono::steady_clock::now(),
288
- dispatch_span = std::move(dispatch_span)](
289
- std::error_code ec,
290
- retry_reason reason,
291
- io::mcbp_message&& msg,
292
- std::optional<key_value_error_map_info> /* error_info */) mutable {
370
+ on_strand([self = this->shared_from_this(),
371
+ start = std::chrono::steady_clock::now(),
372
+ dispatch_span = std::move(dispatch_span)](
373
+ std::error_code ec,
374
+ retry_reason reason,
375
+ io::mcbp_message&& msg,
376
+ std::optional<key_value_error_map_info> /* error_info */) mutable {
293
377
  {
294
378
  const auto server_duration_us =
295
379
  static_cast<std::uint64_t>(protocol::parse_server_duration_us(msg));
@@ -320,17 +404,17 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
320
404
  break;
321
405
  }
322
406
 
323
- auto telemetry_recorder = self->manager_->app_telemetry_meter()->value_recorder(
324
- self->session_->node_uuid(), self->manager_->name());
407
+ auto telemetry_recorder = self->session_->app_telemetry_recorder(
408
+ *self->manager_->app_telemetry_meter(), self->manager_->name());
325
409
  telemetry_recorder->record_latency(category, latency);
326
410
  }
327
411
 
328
412
  #ifdef COUCHBASE_CXX_CLIENT_CREATE_OPERATION_SPAN_IN_CORE
329
413
  {
330
414
  metrics::metric_attributes attrs{
331
- service_type::key_value,
415
+ tracing::service::key_value,
332
416
  self->request.observability_identifier,
333
- ec,
417
+ metrics::standardized_error_type(ec),
334
418
  self->request.id.bucket(),
335
419
  self->request.id.scope(),
336
420
  self->request.id.collection(),
@@ -411,7 +495,7 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
411
495
  } else {
412
496
  io::retry_orchestrator::maybe_retry(self->manager_, self, reason, ec);
413
497
  }
414
- });
498
+ }));
415
499
  }
416
500
 
417
501
  void send_to(io::mcbp_session session)
@@ -464,9 +548,17 @@ private:
464
548
  #endif
465
549
  if (dispatch_span->uses_tags()) {
466
550
  dispatch_span->add_tag(tracing::attributes::dispatch::network_transport, "tcp");
467
- dispatch_span->add_tag(tracing::attributes::dispatch::operation_id,
468
- fmt::format("0x{:x}", request.opaque));
469
- dispatch_span->add_tag(tracing::attributes::dispatch::local_id, session_->id());
551
+ // Prefer the typed capability so the opaque is not formatted to a string on the hot path;
552
+ // spans that do not support it (external tracers) fall back to the string tag.
553
+ if (!dispatch_span->try_set_dispatch_operation_id(request.opaque)) {
554
+ dispatch_span->add_tag(tracing::attributes::dispatch::operation_id,
555
+ fmt::format("0x{:x}", request.opaque));
556
+ }
557
+ // Prefer the typed capability so the local id is not string-tagged on the hot path; external
558
+ // tracers fall back to the string tag.
559
+ if (!dispatch_span->try_set_dispatch_local_id(session_->id())) {
560
+ dispatch_span->add_tag(tracing::attributes::dispatch::local_id, session_->id());
561
+ }
470
562
  }
471
563
  return dispatch_span;
472
564
  }
@@ -475,14 +567,21 @@ private:
475
567
  const std::uint64_t server_duration_us) const
476
568
  {
477
569
  if (dispatch_span->uses_tags()) {
478
- dispatch_span->add_tag(tracing::attributes::dispatch::server_duration, server_duration_us);
570
+ // Prefer the typed capability so the server duration and peer socket are not string-tagged on
571
+ // the hot path; external tracers fall back to the string tags.
572
+ if (!dispatch_span->try_set_dispatch_result(
573
+ server_duration_us, session_->remote_hostname(), session_->remote_port())) {
574
+ dispatch_span->add_tag(tracing::attributes::dispatch::server_duration, server_duration_us);
575
+ dispatch_span->add_tag(tracing::attributes::dispatch::peer_address,
576
+ session_->remote_hostname());
577
+ dispatch_span->add_tag(tracing::attributes::dispatch::peer_port, session_->remote_port());
578
+ }
579
+ // The built-in span does not keep the server (canonical) address; only external tracers
580
+ // consume these, and their SSO-sized tag names do not allocate.
479
581
  dispatch_span->add_tag(tracing::attributes::dispatch::server_address,
480
582
  session_->canonical_hostname());
481
583
  dispatch_span->add_tag(tracing::attributes::dispatch::server_port,
482
584
  session_->canonical_port_number());
483
- dispatch_span->add_tag(tracing::attributes::dispatch::peer_address,
484
- session_->remote_hostname());
485
- dispatch_span->add_tag(tracing::attributes::dispatch::peer_port, session_->remote_port());
486
585
  }
487
586
  dispatch_span->end();
488
587
  }
@@ -0,0 +1,45 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020-2021 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/platform/uuid.h"
21
+ #include "core/protocol/client_opcode.hxx"
22
+
23
+ #include <spdlog/fmt/bundled/core.h>
24
+
25
+ #include <cstdint>
26
+ #include <string>
27
+
28
+ namespace couchbase::core::operations
29
+ {
30
+ /**
31
+ * Build the diagnostic identifier for an mcbp command: the opcode rendered as
32
+ * two lowercase hex digits, a slash, and a random v4 UUID
33
+ * (e.g. "01/3f2a1c9e-...-...", 39 chars).
34
+ *
35
+ * This identifier never travels on the wire; it appears only in log lines,
36
+ * retry traces, and the returned key_value error context. mcbp_command
37
+ * therefore builds it lazily on first use rather than for every operation.
38
+ */
39
+ inline auto
40
+ make_command_id(protocol::client_opcode opcode) -> std::string
41
+ {
42
+ return fmt::format(
43
+ "{:02x}/{}", static_cast<std::uint8_t>(opcode), uuid::to_string(uuid::random()));
44
+ }
45
+ } // namespace couchbase::core::operations
@@ -26,7 +26,15 @@ namespace couchbase::core::io
26
26
  auto
27
27
  binary_header::status() const -> std::uint16_t
28
28
  {
29
- return utils::byte_swap(specific);
29
+ return utils::network_to_host(specific);
30
+ }
31
+
32
+ auto
33
+ binary_header::alt_sizes() const -> binary_header::alt_frame_sizes
34
+ {
35
+ std::array<std::byte, sizeof(keylen)> bytes{};
36
+ std::memcpy(bytes.data(), &keylen, sizeof(keylen));
37
+ return { std::to_integer<std::uint8_t>(bytes[0]), std::to_integer<std::uint8_t>(bytes[1]) };
30
38
  }
31
39
 
32
40
  auto
@@ -18,6 +18,7 @@
18
18
  #pragma once
19
19
 
20
20
  #include <array>
21
+ #include <cstddef>
21
22
  #include <cstdint>
22
23
  #include <vector>
23
24
 
@@ -42,12 +43,30 @@ struct binary_header {
42
43
  std::uint32_t opaque;
43
44
  std::uint64_t cas;
44
45
 
46
+ /**
47
+ * Sizes of the framing extras and key regions of a flexible-framing ("alt") frame.
48
+ *
49
+ * Bytes 2 and 3 of an alt frame header are two independent 1-byte fields, not one 16-bit
50
+ * value. Reinterpreting `keylen` as an integer and shifting the two halves apart puts each
51
+ * byte into the wrong variable on one host order or the other, so read them out as bytes.
52
+ */
53
+ struct alt_frame_sizes {
54
+ std::uint8_t framing_extras;
55
+ std::uint8_t key;
56
+ };
57
+
45
58
  [[nodiscard]] auto status() const -> std::uint16_t;
59
+ [[nodiscard]] auto alt_sizes() const -> alt_frame_sizes;
46
60
  };
47
61
 
48
62
  struct mcbp_message {
49
63
  binary_header header{};
50
64
  std::vector<std::byte> body{};
65
+ // Set on every frame read from the socket, so the buffer that ends up holding `body` can be
66
+ // recycled to the reading thread's pool when the decoded response is destroyed. False only for a
67
+ // message that never passed through the read loop (e.g. a default-constructed one), whose `body`
68
+ // is freed normally.
69
+ bool recycle_body{ false };
51
70
 
52
71
  mcbp_message() = default;
53
72
  mcbp_message(const mcbp_message& other) = delete;