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
@@ -19,14 +19,16 @@
19
19
 
20
20
  #include <couchbase/build_config.hxx>
21
21
 
22
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
23
- #include "core/columnar/background_bootstrap_listener.hxx"
24
- #endif
22
+ #include "configuration_belongs_to_session.hxx"
23
+ #include "opaque_ring_table.hxx"
24
+
25
+ #include "core/app_telemetry_meter.hxx"
25
26
  #include "core/config_listener.hxx"
26
27
  #include "core/diagnostics.hxx"
27
28
  #include "core/impl/bootstrap_error.hxx"
28
29
  #include "core/impl/bootstrap_state_listener.hxx"
29
30
  #include "core/logger/logger.hxx"
31
+ #include "core/logger/redaction.hxx"
30
32
  #include "core/mcbp/codec.hxx"
31
33
  #include "core/mcbp/queue_request.hxx"
32
34
  #include "core/meta/version.hxx"
@@ -50,8 +52,10 @@
50
52
  #include "core/sasl/error_fmt.h"
51
53
  #include "core/topology/capabilities_fmt.hxx"
52
54
  #include "core/topology/configuration_fmt.hxx"
55
+ #include "mcbp_buffer_pool.hxx"
53
56
  #include "mcbp_context.hxx"
54
57
  #include "mcbp_message.hxx"
58
+ #include "mcbp_output_queue.hxx"
55
59
  #include "mcbp_parser.hxx"
56
60
  #include "retry_orchestrator.hxx"
57
61
  #include "streams.hxx"
@@ -109,7 +113,7 @@ struct mcbp_header_layout {
109
113
  if (magic == 0x18 || magic == 0x08) {
110
114
  return keylen.alt.key;
111
115
  }
112
- return couchbase::core::utils::byte_swap(keylen.normal);
116
+ return couchbase::core::utils::network_to_host(keylen.normal);
113
117
  }
114
118
 
115
119
  [[nodiscard]] constexpr auto framing_extras_length() const -> std::uint8_t
@@ -149,10 +153,10 @@ struct fmt::formatter<mcbp_header_view<Container>> {
149
153
  header->extlen,
150
154
  header->datatype,
151
155
  header->specific_name(),
152
- couchbase::core::utils::byte_swap(header->specific),
153
- couchbase::core::utils::byte_swap(header->bodylen),
154
- couchbase::core::utils::byte_swap(header->opaque),
155
- couchbase::core::utils::byte_swap(header->cas));
156
+ couchbase::core::utils::network_to_host(header->specific),
157
+ couchbase::core::utils::network_to_host(header->bodylen),
158
+ couchbase::core::utils::network_to_host(header->opaque),
159
+ couchbase::core::utils::network_to_host(header->cas));
156
160
  }
157
161
  };
158
162
 
@@ -269,13 +273,21 @@ class mcbp_session_impl
269
273
  hello_req.body().enable_mutation_tokens();
270
274
  }
271
275
  hello_req.opaque(session_->next_opaque());
272
- auto user_agent = meta::user_agent_for_mcbp(
273
- session_->client_id_, session_->id_, session_->origin_.options().user_agent_extra, 250);
276
+ const auto& user_agent_extra = session_->origin_.options().user_agent_extra;
277
+ auto user_agent =
278
+ meta::user_agent_for_mcbp(session_->client_id_, session_->id_, user_agent_extra, 250);
274
279
  hello_req.body().user_agent(user_agent);
275
- CB_LOG_DEBUG("{} user_agent={}, requested_features=[{}]",
276
- session_->log_prefix_,
277
- user_agent,
278
- utils::join_strings_fmt(hello_req.body().features(), ", "));
280
+ CB_LOG_DEBUG(
281
+ "{} user_agent={}, requested_features=[{}]",
282
+ session_->log_prefix_,
283
+ // The blob folds in the application's user_agent_extra, and that is the only user data it
284
+ // can hold: the rest is SDK, platform and TLS identity, and its "i" field is the client
285
+ // and session id that log_prefix_ already prints in the clear on this same line. So it is
286
+ // tagged only when there is an extra to protect, which keeps the version block readable in
287
+ // the logs support actually receives.
288
+ logger::user_data_if(!user_agent_extra.empty(), user_agent),
289
+ // protocol feature names, not a document body
290
+ logger::not_sensitive(utils::join_strings_fmt(hello_req.body().features(), ", ")));
279
291
  session_->write(hello_req.data());
280
292
 
281
293
  if (!session_->origin_.credentials().uses_certificate()) {
@@ -370,7 +382,10 @@ class mcbp_session_impl
370
382
  case protocol::client_opcode::hello: {
371
383
  protocol::client_response<protocol::hello_response_body> resp(std::move(msg));
372
384
  if (resp.status() == key_value_status_code::success) {
373
- session_->supported_features_ = resp.body().supported_features();
385
+ {
386
+ const std::scoped_lock lock(session_->session_info_mutex_);
387
+ session_->supported_features_ = resp.body().supported_features();
388
+ }
374
389
  CB_LOG_DEBUG("{} supported_features=[{}]",
375
390
  session_->log_prefix_,
376
391
  utils::join_strings_fmt(session_->supported_features_, ", "));
@@ -495,7 +510,7 @@ class mcbp_session_impl
495
510
  if (resp.status() == key_value_status_code::success) {
496
511
  CB_LOG_DEBUG("{} selected bucket: {}",
497
512
  session_->log_prefix_,
498
- session_->bucket_name_.value_or(""));
513
+ logger::metadata(session_->bucket_name_.value_or("")));
499
514
  session_->bucket_selected_ = true;
500
515
  } else if (resp.status() == key_value_status_code::not_found) {
501
516
  auto error_msg =
@@ -543,12 +558,14 @@ class mcbp_session_impl
543
558
  if (session_->origin_.options().dump_configuration) {
544
559
  if (const auto& text = resp.body().config_text(); text.has_value()) {
545
560
  CB_LOG_TRACE("{} configuration from get_cluster_config request (bootstrap, "
546
- "size={}, endpoint=\"{}:{}\"), {}",
561
+ "size={}, endpoint=\"{}\"), {}",
547
562
  session_->log_prefix_,
548
563
  text.value().size(),
549
- info.endpoint_address,
550
- info.endpoint_port,
551
- text.value());
564
+ logger::system_data(
565
+ fmt::format("{}:{}", info.endpoint_address, info.endpoint_port)),
566
+ // an opt-in dump, it exists to show the exact bytes. That is the
567
+ // "none at all" case of the rule in redaction.hxx.
568
+ logger::not_redacted(text.value()));
552
569
  }
553
570
  }
554
571
  if (resp.status() == key_value_status_code::success) {
@@ -625,44 +642,42 @@ class mcbp_session_impl
625
642
  if (session_->origin_.options().dump_configuration) {
626
643
  if (const auto& text = req.body().config_text(); text.has_value()) {
627
644
  CB_LOG_TRACE("{} configuration from cluster_map_change_notification request "
628
- "(size={}, endpoint=\"{}:{}\"), {}",
645
+ "(size={}, endpoint=\"{}\"), {}",
629
646
  session_->log_prefix_,
630
647
  text.value().size(),
631
- info.endpoint_address,
632
- info.endpoint_port,
633
- text.value());
648
+ logger::system_data(
649
+ fmt::format("{}:{}", info.endpoint_address, info.endpoint_port)),
650
+ // an opt-in dump, it exists to show the exact bytes. That is the
651
+ // "none at all" case of the rule in redaction.hxx.
652
+ logger::not_redacted(text.value()));
634
653
  }
635
654
  }
636
655
  std::optional<topology::configuration> config = req.body().config();
637
- if (session_ && config.has_value()) {
638
- if ((!config->bucket.has_value() && req.body().bucket().empty()) ||
639
- (session_->bucket_name_.has_value() && !req.body().bucket().empty() &&
640
- // TODO(CXXCBC-549)
641
- // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
642
- session_->bucket_name_.value() == req.body().bucket())) {
643
- session_->update_configuration(std::move(config.value()));
644
- }
656
+ if (session_ && config.has_value() &&
657
+ configuration_belongs_to_session(config->bucket, session_->bucket_name_)) {
658
+ session_->update_configuration(std::move(config.value()));
645
659
  }
646
660
  } break;
647
661
  default:
648
- CB_LOG_WARNING("{} unexpected server request: opcode={:x}, opaque={}{:a}{:a}",
662
+ CB_LOG_WARNING("{} unexpected server request: opcode={:x}, opaque={}{:a}",
649
663
  session_->log_prefix_,
650
664
  msg.header.opcode,
651
- utils::byte_swap(msg.header.opaque),
652
- spdlog::to_hex(msg.header_data()),
653
- spdlog::to_hex(msg.body));
665
+ utils::network_to_host(msg.header.opaque),
666
+ // No need to dump the body here, CB_LOG_PROTOCOL has the raw bytes.
667
+ spdlog::to_hex(msg.header_data()));
654
668
  }
655
669
  break;
656
670
  case protocol::magic::client_request:
657
671
  case protocol::magic::alt_client_request:
658
672
  case protocol::magic::server_response:
659
- CB_LOG_WARNING("{} unexpected magic: {} (opcode={:x}, opaque={}){:a}{:a}",
673
+ case protocol::magic::invalid:
674
+ CB_LOG_WARNING("{} unexpected magic: {} (opcode={:x}, opaque={}){:a}",
660
675
  session_->log_prefix_,
661
676
  magic,
662
677
  msg.header.opcode,
663
- utils::byte_swap(msg.header.opaque),
664
- spdlog::to_hex(msg.header_data()),
665
- spdlog::to_hex(msg.body));
678
+ utils::network_to_host(msg.header.opaque),
679
+ // No need to dump the body here, CB_LOG_PROTOCOL has the raw bytes.
680
+ spdlog::to_hex(msg.header_data()));
666
681
  break;
667
682
  }
668
683
  }
@@ -717,12 +732,14 @@ class mcbp_session_impl
717
732
  if (session_->origin_.options().dump_configuration) {
718
733
  if (const auto& text = resp.body().config_text(); text.has_value()) {
719
734
  CB_LOG_TRACE("{} configuration from get_cluster_config response (size={}, "
720
- "endpoint=\"{}:{}\"), {}",
735
+ "endpoint=\"{}\"), {}",
721
736
  session_->log_prefix_,
722
737
  text.value().size(),
723
- info.endpoint_address,
724
- info.endpoint_port,
725
- text.value());
738
+ logger::system_data(
739
+ fmt::format("{}:{}", info.endpoint_address, info.endpoint_port)),
740
+ // an opt-in dump, it exists to show the exact bytes. That is the
741
+ // "none at all" case of the rule in redaction.hxx.
742
+ logger::not_redacted(text.value()));
726
743
  }
727
744
  }
728
745
  if (resp.status() == key_value_status_code::success) {
@@ -767,7 +784,7 @@ class mcbp_session_impl
767
784
  case protocol::client_opcode::subdoc_multi_lookup:
768
785
  case protocol::client_opcode::subdoc_multi_mutation:
769
786
  case protocol::client_opcode::sasl_auth: {
770
- const std::uint16_t status = utils::byte_swap(msg.header.specific);
787
+ const std::uint16_t status = utils::network_to_host(msg.header.specific);
771
788
  if (status == static_cast<std::uint16_t>(key_value_status_code::not_my_vbucket)) {
772
789
  session_->handle_not_my_vbucket(msg);
773
790
  }
@@ -775,12 +792,12 @@ class mcbp_session_impl
775
792
  CB_LOG_WARNING("{} received auth stale status for {}, opaque={}",
776
793
  session_->log_prefix_,
777
794
  protocol::client_opcode(msg.header.opcode),
778
- utils::byte_swap(msg.header.opaque));
795
+ utils::network_to_host(msg.header.opaque));
779
796
  session_->stop(retry_reason::do_not_retry);
780
797
  return;
781
798
  }
782
799
 
783
- std::uint32_t opaque = utils::byte_swap(msg.header.opaque);
800
+ std::uint32_t opaque = utils::network_to_host(msg.header.opaque);
784
801
  if (session_->handle_request(opcode, status, opaque, std::move(msg))) {
785
802
  CB_LOG_TRACE("{} MCBP invoked operation handler: opcode={}, opaque={}, status={}",
786
803
  session_->log_prefix_,
@@ -796,12 +813,12 @@ class mcbp_session_impl
796
813
  }
797
814
  } break;
798
815
  default:
799
- CB_LOG_WARNING("{} unexpected client response: opcode={}, opaque={}{:a}{:a})",
816
+ CB_LOG_WARNING("{} unexpected client response: opcode={}, opaque={}{:a})",
800
817
  session_->log_prefix_,
801
818
  opcode,
802
819
  msg.header.opaque,
803
- spdlog::to_hex(msg.header_data()),
804
- spdlog::to_hex(msg.body));
820
+ // No need to dump the body here, CB_LOG_PROTOCOL has the raw bytes.
821
+ spdlog::to_hex(msg.header_data()));
805
822
  }
806
823
  break;
807
824
  case protocol::magic::server_request:
@@ -815,44 +832,42 @@ class mcbp_session_impl
815
832
  if (session_->origin_.options().dump_configuration) {
816
833
  if (const auto& text = req.body().config_text(); text.has_value()) {
817
834
  CB_LOG_TRACE("{} configuration from cluster_map_change_notification request "
818
- "(size={}, endpoint=\"{}:{}\"), {}",
835
+ "(size={}, endpoint=\"{}\"), {}",
819
836
  session_->log_prefix_,
820
837
  text.value().size(),
821
- info.endpoint_address,
822
- info.endpoint_port,
823
- text.value());
838
+ logger::system_data(
839
+ fmt::format("{}:{}", info.endpoint_address, info.endpoint_port)),
840
+ // an opt-in dump, it exists to show the exact bytes. That is the
841
+ // "none at all" case of the rule in redaction.hxx.
842
+ logger::not_redacted(text.value()));
824
843
  }
825
844
  }
826
845
  std::optional<topology::configuration> config = req.body().config();
827
- if (session_ && config.has_value()) {
828
- if ((!config->bucket.has_value() && req.body().bucket().empty()) ||
829
- (session_->bucket_name_.has_value() && !req.body().bucket().empty() &&
830
- // TODO(CXXCBC-549)
831
- // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
832
- session_->bucket_name_.value() == req.body().bucket())) {
833
- session_->update_configuration(std::move(config.value()));
834
- }
846
+ if (session_ && config.has_value() &&
847
+ configuration_belongs_to_session(config->bucket, session_->bucket_name_)) {
848
+ session_->update_configuration(std::move(config.value()));
835
849
  }
836
850
  } break;
837
851
  default:
838
- CB_LOG_WARNING("{} unexpected server request: opcode={:x}, opaque={}{:a}{:a}",
852
+ CB_LOG_WARNING("{} unexpected server request: opcode={:x}, opaque={}{:a}",
839
853
  session_->log_prefix_,
840
854
  msg.header.opcode,
841
855
  msg.header.opaque,
842
- spdlog::to_hex(msg.header_data()),
843
- spdlog::to_hex(msg.body));
856
+ // No need to dump the body here, CB_LOG_PROTOCOL has the raw bytes.
857
+ spdlog::to_hex(msg.header_data()));
844
858
  }
845
859
  break;
846
860
  case protocol::magic::client_request:
847
861
  case protocol::magic::alt_client_request:
848
862
  case protocol::magic::server_response:
849
- CB_LOG_WARNING("{} unexpected magic: {} (opcode={:x}, opaque={}){:a}{:a}",
863
+ case protocol::magic::invalid:
864
+ CB_LOG_WARNING("{} unexpected magic: {} (opcode={:x}, opaque={}){:a}",
850
865
  session_->log_prefix_,
851
866
  magic,
852
867
  msg.header.opcode,
853
868
  msg.header.opaque,
854
- spdlog::to_hex(msg.header_data()),
855
- spdlog::to_hex(msg.body));
869
+ // No need to dump the body here, CB_LOG_PROTOCOL has the raw bytes.
870
+ spdlog::to_hex(msg.header_data()));
856
871
  break;
857
872
  }
858
873
  }
@@ -886,7 +901,7 @@ public:
886
901
  , codec_{ { supported_features_.begin(), supported_features_.end() } }
887
902
  {
888
903
  log_prefix_ = fmt::format(
889
- "[{}/{}/{}/{}]", client_id_, id_, stream_->log_prefix(), bucket_name_.value_or("-"));
904
+ "[{}/{}/{}/{}]", client_id_, id_, stream_->log_prefix(), log_prefix_bucket_name());
890
905
  }
891
906
 
892
907
  mcbp_session_impl(std::string_view client_id,
@@ -916,7 +931,7 @@ public:
916
931
  , codec_{ { supported_features_.begin(), supported_features_.end() } }
917
932
  {
918
933
  log_prefix_ = fmt::format(
919
- "[{}/{}/{}/{}]", client_id_, id_, stream_->log_prefix(), bucket_name_.value_or("-"));
934
+ "[{}/{}/{}/{}]", client_id_, id_, stream_->log_prefix(), log_prefix_bucket_name());
920
935
  }
921
936
 
922
937
  mcbp_session_impl(const mcbp_session_impl&) = delete;
@@ -930,8 +945,9 @@ public:
930
945
  stop(retry_reason::do_not_retry);
931
946
  }
932
947
 
933
- [[nodiscard]] auto log_prefix() const -> const std::string&
948
+ [[nodiscard]] auto log_prefix() const -> std::string
934
949
  {
950
+ const std::scoped_lock lock(session_info_mutex_);
935
951
  return log_prefix_;
936
952
  }
937
953
 
@@ -1063,6 +1079,7 @@ public:
1063
1079
 
1064
1080
  [[nodiscard]] auto context() const -> mcbp_context
1065
1081
  {
1082
+ const std::scoped_lock lock(session_info_mutex_);
1066
1083
  return { config_, supported_features_ };
1067
1084
  }
1068
1085
 
@@ -1159,7 +1176,8 @@ public:
1159
1176
  }
1160
1177
 
1161
1178
  if (reason == retry_reason::do_not_retry || reason == retry_reason::unknown) {
1162
- CB_LOG_WARNING("{} reauthentication failed (code={}, message={}, reason={}, opaque={})",
1179
+ CB_LOG_WARNING("{} reauthentication failed (code={}, message={}, reason={}, opaque={}), "
1180
+ "closing connection",
1163
1181
  self->log_prefix_,
1164
1182
  ec.value(),
1165
1183
  ec.message(),
@@ -1167,6 +1185,7 @@ public:
1167
1185
  req.opaque());
1168
1186
  self->reauth_deadline_.cancel();
1169
1187
  self->reauth_in_progress_ = false;
1188
+ self->stop(retry_reason::do_not_retry);
1170
1189
  return;
1171
1190
  }
1172
1191
 
@@ -1208,45 +1227,6 @@ public:
1208
1227
  self->state_listener_->report_bootstrap_error(
1209
1228
  fmt::format("{}:{}", self->bootstrap_hostname_, self->bootstrap_port_), ec);
1210
1229
  }
1211
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
1212
- // an existing background bootstrap listener means we want to infinitely
1213
- // attempt to connect.
1214
- if (self->background_bootstrap_listener_) {
1215
- if (self->last_bootstrap_error_.has_value()) {
1216
- self->background_bootstrap_listener_->notify_bootstrap_error(
1217
- self->last_bootstrap_error_.value());
1218
- } else {
1219
- self->background_bootstrap_listener_->notify_bootstrap_error(
1220
- { make_error_code(errc::common::unambiguous_timeout),
1221
- "Unable to connect in time.",
1222
- self->bootstrap_hostname_,
1223
- self->bootstrap_port_ });
1224
- }
1225
- auto backoff = std::chrono::milliseconds(500);
1226
- CB_LOG_DEBUG("{} unable to connect in time, waiting for {}ms before retry",
1227
- self->log_prefix_,
1228
- backoff.count());
1229
- self->retry_backoff_.expires_after(backoff);
1230
- self->retry_backoff_.async_wait([self](std::error_code ec) mutable {
1231
- if (ec == asio::error::operation_aborted || self->stopped_) {
1232
- return;
1233
- }
1234
- self->origin_.restart();
1235
- self->initiate_bootstrap();
1236
- });
1237
- } else {
1238
- if (!ec) {
1239
- ec = errc::common::unambiguous_timeout;
1240
- }
1241
- CB_LOG_WARNING("{} unable to bootstrap in time, bootstrap_timeout: {}",
1242
- self->log_prefix_,
1243
- bootstrap_timeout);
1244
- if (auto h = std::move(self->bootstrap_callback_); h) {
1245
- h(ec, {});
1246
- }
1247
- self->stop(retry_reason::do_not_retry);
1248
- }
1249
- #else
1250
1230
  if (!ec) {
1251
1231
  ec = errc::common::unambiguous_timeout;
1252
1232
  }
@@ -1257,7 +1237,6 @@ public:
1257
1237
  h(ec, {});
1258
1238
  }
1259
1239
  self->stop(retry_reason::do_not_retry);
1260
- #endif
1261
1240
  });
1262
1241
  initiate_bootstrap();
1263
1242
  }
@@ -1278,25 +1257,12 @@ public:
1278
1257
  self->log_prefix_,
1279
1258
  old_id,
1280
1259
  self->stream_->id(),
1281
- self->bootstrap_hostname_,
1282
- self->bootstrap_port_);
1260
+ logger::system_data(self->bootstrap_hostname_),
1261
+ logger::system_data(self->bootstrap_port_));
1283
1262
  return self->initiate_bootstrap();
1284
1263
  });
1285
1264
  }
1286
1265
  if (origin_.exhausted()) {
1287
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
1288
- if (background_bootstrap_listener_) {
1289
- if (last_bootstrap_error_.has_value()) {
1290
- background_bootstrap_listener_->notify_bootstrap_error(last_bootstrap_error_.value());
1291
- } else {
1292
- background_bootstrap_listener_->notify_bootstrap_error(
1293
- { errc::network::no_endpoints_left,
1294
- "Reached end of list of bootstrap nodes.",
1295
- bootstrap_hostname_,
1296
- bootstrap_port_ });
1297
- }
1298
- }
1299
- #endif
1300
1266
  auto backoff = std::chrono::milliseconds(500);
1301
1267
  CB_LOG_DEBUG("{} reached the end of list of bootstrap nodes, waiting for {}ms before restart",
1302
1268
  log_prefix_,
@@ -1315,12 +1281,15 @@ public:
1315
1281
  bootstrap_port_number_ =
1316
1282
  gsl::narrow_cast<std::uint16_t>(std::stoul(bootstrap_port_, nullptr, 10));
1317
1283
  bootstrap_address_ = fmt::format("{}:{}", bootstrap_hostname_, bootstrap_port_);
1318
- log_prefix_ = fmt::format("[{}/{}/{}/{}] <{}>",
1319
- client_id_,
1320
- id_,
1321
- stream_->log_prefix(),
1322
- bucket_name_.value_or("-"),
1323
- bootstrap_address_);
1284
+ {
1285
+ const std::scoped_lock lock(session_info_mutex_);
1286
+ log_prefix_ = fmt::format("[{}/{}/{}/{}] <{}>",
1287
+ client_id_,
1288
+ id_,
1289
+ stream_->log_prefix(),
1290
+ log_prefix_bucket_name(),
1291
+ logger::system_data(bootstrap_address_));
1292
+ }
1324
1293
  CB_LOG_DEBUG("{} attempt to establish MCBP connection", log_prefix_);
1325
1294
 
1326
1295
  resolve_deadline_.expires_after(origin_.options().resolve_timeout);
@@ -1349,6 +1318,12 @@ public:
1349
1318
  return node_uuid_;
1350
1319
  }
1351
1320
 
1321
+ auto app_telemetry_recorder(app_telemetry_meter& meter, const std::string& bucket_name)
1322
+ -> std::shared_ptr<app_telemetry_value_recorder>
1323
+ {
1324
+ return app_telemetry_recorder_cache_.value_recorder(meter, node_uuid_, bucket_name);
1325
+ }
1326
+
1352
1327
  [[nodiscard]] auto is_stopped() const -> bool
1353
1328
  {
1354
1329
  return stopped_;
@@ -1371,33 +1346,48 @@ public:
1371
1346
  }
1372
1347
 
1373
1348
  if (reason == retry_reason::socket_closed_while_in_flight && !bootstrapped_) {
1374
- return stream_->close([self = shared_from_this(), old_id = stream_->id()](std::error_code) {
1375
- CB_LOG_DEBUG(
1376
- R"({} reopened socket connection due to IO error, "{}" -> "{}", host="{}", port={})",
1377
- self->log_prefix_,
1378
- old_id,
1379
- self->stream_->id(),
1380
- self->bootstrap_hostname_,
1381
- self->bootstrap_port_);
1382
- return self->initiate_bootstrap();
1383
- });
1349
+ // Dispatch close through the stream's strand to serialize with any in-flight on_connect
1350
+ // that may be reading plain_stream_impl::stream_ concurrently.
1351
+ return asio::dispatch(
1352
+ stream_->get_executor(),
1353
+ [self = shared_from_this(), old_id = stream_->id()]() mutable -> void {
1354
+ self->stream_->close([self, old_id = std::move(old_id)](std::error_code) mutable -> void {
1355
+ CB_LOG_DEBUG(
1356
+ R"({} reopened socket connection due to IO error, "{}" -> "{}", host="{}", port={})",
1357
+ self->log_prefix_,
1358
+ old_id,
1359
+ self->stream_->id(),
1360
+ logger::system_data(self->bootstrap_hostname_),
1361
+ logger::system_data(self->bootstrap_port_));
1362
+ return self->initiate_bootstrap();
1363
+ });
1364
+ });
1384
1365
  }
1385
1366
 
1386
1367
  state_ = diag::endpoint_state::disconnecting;
1387
- CB_LOG_DEBUG("{} stop MCBP connection, reason={}", log_prefix_, reason);
1368
+ std::string stop_log_prefix;
1369
+ {
1370
+ const std::scoped_lock lock(session_info_mutex_);
1371
+ stop_log_prefix = log_prefix_;
1372
+ }
1373
+ CB_LOG_DEBUG("{} stop MCBP connection, reason={}", stop_log_prefix, reason);
1388
1374
  stopped_ = true;
1389
- bootstrap_deadline_.cancel();
1390
- resolve_deadline_.cancel();
1391
- connection_deadline_.cancel();
1392
- reauth_deadline_.cancel();
1393
- retry_backoff_.cancel();
1394
- ping_timeout_.cancel();
1395
- resolver_.cancel();
1396
- stream_->close([](std::error_code) {
1375
+ // Dispatch timer cancellation and stream close through the strand to avoid
1376
+ // racing with on_connect() and bootstrap_handler which run on the IO thread.
1377
+ asio::dispatch(stream_->get_executor(), [self = shared_from_this()]() -> void {
1378
+ self->bootstrap_deadline_.cancel();
1379
+ self->resolve_deadline_.cancel();
1380
+ self->connection_deadline_.cancel();
1381
+ self->reauth_deadline_.cancel();
1382
+ self->retry_backoff_.cancel();
1383
+ self->ping_timeout_.cancel();
1384
+ self->resolver_.cancel();
1385
+ if (auto h = std::move(self->bootstrap_handler_); h) {
1386
+ h->stop();
1387
+ }
1388
+ self->stream_->close([](std::error_code) -> void {
1389
+ });
1397
1390
  });
1398
- if (auto h = std::move(bootstrap_handler_); h) {
1399
- h->stop();
1400
- }
1401
1391
  if (auto h = std::move(handler_); h) {
1402
1392
  h->stop();
1403
1393
  }
@@ -1408,18 +1398,23 @@ public:
1408
1398
  }
1409
1399
  }
1410
1400
  {
1411
- const std::scoped_lock lock(command_handlers_mutex_);
1412
- for (auto& [opaque, handler] : command_handlers_) {
1401
+ // Drain under the lock, then invoke the handlers after releasing it: a command handler may
1402
+ // re-enter the session (as cancel() documents), so running one while holding
1403
+ // command_handlers_mutex_ could deadlock during shutdown.
1404
+ auto cancelled = [this] {
1405
+ const std::scoped_lock lock(command_handlers_mutex_);
1406
+ return command_handlers_.drain();
1407
+ }();
1408
+ for (auto& [opaque, handler] : cancelled) {
1413
1409
  if (handler) {
1414
1410
  CB_LOG_DEBUG("{} MCBP cancel operation during session close, opaque={}, ec={}",
1415
- log_prefix_,
1411
+ stop_log_prefix,
1416
1412
  opaque,
1417
1413
  ec.message());
1418
1414
  auto fun = std::move(handler);
1419
1415
  fun(ec, reason, {}, {});
1420
1416
  }
1421
1417
  }
1422
- command_handlers_.clear();
1423
1418
  }
1424
1419
  {
1425
1420
  const std::scoped_lock lock(operations_mutex_);
@@ -1428,24 +1423,23 @@ public:
1428
1423
  auto& [request, handler] = operation;
1429
1424
  if (handler) {
1430
1425
  CB_LOG_DEBUG("{} MCBP cancel operation during session close, opaque={}, ec={}",
1431
- log_prefix_,
1426
+ stop_log_prefix,
1432
1427
  opaque,
1433
1428
  ec.message());
1434
- handler->handle_response(std::move(request), {}, reason, {}, {});
1429
+ handler->handle_response(
1430
+ std::move(request), io::mcbp_session(shared_from_this()), ec, reason, {}, {});
1435
1431
  }
1436
1432
  }
1437
1433
  operations_.clear();
1438
1434
  }
1439
- config_listeners_.clear();
1435
+ {
1436
+ const std::scoped_lock lock(session_info_mutex_);
1437
+ config_listeners_.clear();
1438
+ }
1440
1439
  state_ = diag::endpoint_state::disconnected;
1441
1440
  if (auto on_stop = std::move(on_stop_handler_); on_stop) {
1442
1441
  on_stop();
1443
1442
  }
1444
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
1445
- if (background_bootstrap_listener_) {
1446
- background_bootstrap_listener_ = nullptr;
1447
- }
1448
- #endif
1449
1443
  }
1450
1444
 
1451
1445
  void write(std::vector<std::byte>&& buf)
@@ -1454,8 +1448,7 @@ public:
1454
1448
  return;
1455
1449
  }
1456
1450
  CB_LOG_TRACE("{} MCBP send {}", log_prefix_, mcbp_header_view(buf));
1457
- const std::scoped_lock lock(output_buffer_mutex_);
1458
- output_buffer_.emplace_back(std::move(buf));
1451
+ output_queue_.stage(std::move(buf));
1459
1452
  }
1460
1453
 
1461
1454
  void flush()
@@ -1463,9 +1456,13 @@ public:
1463
1456
  if (stopped_) {
1464
1457
  return;
1465
1458
  }
1466
- asio::post(asio::bind_executor(ctx_, [self = shared_from_this()]() {
1467
- self->do_write();
1468
- }));
1459
+ // Only post when the queue was idle; while a write is scheduled or in flight the completion
1460
+ // handler re-drives do_write, so an extra post would be wasted work on the hot path.
1461
+ if (output_queue_.mark_for_dispatch()) {
1462
+ asio::post(asio::bind_executor(ctx_, [self = shared_from_this()]() {
1463
+ self->do_write();
1464
+ }));
1465
+ }
1469
1466
  }
1470
1467
 
1471
1468
  void write_and_flush(std::vector<std::byte>&& buf)
@@ -1473,8 +1470,13 @@ public:
1473
1470
  if (stopped_) {
1474
1471
  return;
1475
1472
  }
1476
- write(std::move(buf));
1477
- flush();
1473
+ CB_LOG_TRACE("{} MCBP send {}", log_prefix_, mcbp_header_view(buf));
1474
+ // Stage the buffer and post do_write only on the idle -> scheduled transition (single lock).
1475
+ if (output_queue_.enqueue(std::move(buf))) {
1476
+ asio::post(asio::bind_executor(ctx_, [self = shared_from_this()]() {
1477
+ self->do_write();
1478
+ }));
1479
+ }
1478
1480
  }
1479
1481
 
1480
1482
  void remove_request(std::shared_ptr<mcbp::queue_request> request) override
@@ -1503,11 +1505,7 @@ public:
1503
1505
  command_handler fun{};
1504
1506
  {
1505
1507
  const std::scoped_lock lock(command_handlers_mutex_);
1506
- if (auto handler = command_handlers_.find(opaque);
1507
- handler != command_handlers_.end() && handler->second) {
1508
- fun = std::move(handler->second);
1509
- command_handlers_.erase(handler);
1510
- }
1508
+ fun = command_handlers_.take(opaque);
1511
1509
  }
1512
1510
 
1513
1511
  auto reason = status == static_cast<std::uint16_t>(key_value_status_code::not_my_vbucket)
@@ -1536,6 +1534,7 @@ public:
1536
1534
  }
1537
1535
  if (request) {
1538
1536
  handler->handle_response(std::move(request),
1537
+ io::mcbp_session(shared_from_this()),
1539
1538
  protocol::map_status_code(opcode, status),
1540
1539
  reason,
1541
1540
  std::move(msg),
@@ -1560,6 +1559,7 @@ public:
1560
1559
  CB_LOG_WARNING("cancel operation while trying to write to closed mcbp session, opaque={}",
1561
1560
  opaque);
1562
1561
  handler->handle_response(request,
1562
+ io::mcbp_session(shared_from_this()),
1563
1563
  errc::common::request_canceled,
1564
1564
  retry_reason::socket_closed_while_in_flight,
1565
1565
  {},
@@ -1595,7 +1595,7 @@ public:
1595
1595
  }
1596
1596
  {
1597
1597
  const std::scoped_lock lock(command_handlers_mutex_);
1598
- command_handlers_.try_emplace(opaque, std::move(handler));
1598
+ command_handlers_.insert(opaque, std::move(handler));
1599
1599
  }
1600
1600
  if (bootstrapped_ && stream_->is_open()) {
1601
1601
  write_and_flush(std::move(data));
@@ -1617,33 +1617,34 @@ public:
1617
1617
  if (stopped_) {
1618
1618
  return false;
1619
1619
  }
1620
- command_handlers_mutex_.lock();
1621
- if (auto handler = command_handlers_.find(opaque); handler != command_handlers_.end()) {
1622
- CB_LOG_DEBUG("{} MCBP cancel operation, opaque={}, ec={} ({})",
1623
- log_prefix_,
1624
- opaque,
1625
- ec.value(),
1626
- ec.message());
1627
- if (handler->second) {
1628
- auto fun = std::move(handler->second);
1629
- command_handlers_.erase(handler);
1630
- command_handlers_mutex_.unlock();
1631
- fun(ec, reason, {}, {});
1632
- return true;
1633
- }
1620
+ std::unique_lock lock(command_handlers_mutex_);
1621
+ auto fun = command_handlers_.take(opaque);
1622
+ if (!fun) {
1623
+ return false;
1634
1624
  }
1635
- command_handlers_mutex_.unlock();
1636
- return false;
1625
+ CB_LOG_DEBUG("{} MCBP cancel operation, opaque={}, ec={} ({})",
1626
+ log_prefix_,
1627
+ opaque,
1628
+ ec.value(),
1629
+ ec.message());
1630
+ // Release before invoking the handler: it must not run while holding the command-handlers
1631
+ // lock (it may re-enter the session), and the unique_lock guarantees the mutex is freed on
1632
+ // every path -- including an early return or an exception before this point.
1633
+ lock.unlock();
1634
+ fun(ec, reason, {}, {});
1635
+ return true;
1637
1636
  }
1638
1637
 
1639
1638
  [[nodiscard]] auto supports_feature(protocol::hello_feature feature) -> bool
1640
1639
  {
1640
+ const std::scoped_lock lock(session_info_mutex_);
1641
1641
  return std::find(supported_features_.begin(), supported_features_.end(), feature) !=
1642
1642
  supported_features_.end();
1643
1643
  }
1644
1644
 
1645
1645
  [[nodiscard]] auto supported_features() const -> std::vector<protocol::hello_feature>
1646
1646
  {
1647
+ const std::scoped_lock lock(session_info_mutex_);
1647
1648
  return supported_features_;
1648
1649
  }
1649
1650
 
@@ -1713,16 +1714,6 @@ public:
1713
1714
  return ++opaque_;
1714
1715
  }
1715
1716
 
1716
- [[nodiscard]] auto last_bootstrap_error() && -> std::optional<impl::bootstrap_error>
1717
- {
1718
- return std::move(last_bootstrap_error_);
1719
- }
1720
-
1721
- [[nodiscard]] auto last_bootstrap_error() const& -> const std::optional<impl::bootstrap_error>&
1722
- {
1723
- return last_bootstrap_error_;
1724
- }
1725
-
1726
1717
  auto decode_error_code(std::uint16_t code) -> std::optional<key_value_error_map_info>
1727
1718
  {
1728
1719
  if (error_map_) {
@@ -1736,6 +1727,7 @@ public:
1736
1727
 
1737
1728
  void on_configuration_update(std::shared_ptr<config_listener> handler)
1738
1729
  {
1730
+ const std::scoped_lock lock(session_info_mutex_);
1739
1731
  config_listeners_.emplace_back(std::move(handler));
1740
1732
  }
1741
1733
 
@@ -1799,10 +1791,13 @@ public:
1799
1791
  config_.reset();
1800
1792
  config_.emplace(std::move(config));
1801
1793
  configured_ = true;
1802
- for (const auto& listener : config_listeners_) {
1803
- asio::post(asio::bind_executor(ctx_, [listener, c = config_.value()]() mutable {
1804
- return listener->update_config(std::move(c));
1805
- }));
1794
+ {
1795
+ const std::scoped_lock listeners_lock(session_info_mutex_);
1796
+ for (const auto& listener : config_listeners_) {
1797
+ asio::post(asio::bind_executor(ctx_, [listener, c = config_.value()]() mutable {
1798
+ return listener->update_config(std::move(c));
1799
+ }));
1800
+ }
1806
1801
  }
1807
1802
  }
1808
1803
 
@@ -1817,25 +1812,27 @@ public:
1817
1812
  auto magic = static_cast<protocol::magic>(msg.header.magic);
1818
1813
  const std::uint8_t extras_size = msg.header.extlen;
1819
1814
  std::uint8_t framing_extras_size = 0;
1820
- std::uint16_t key_size = utils::byte_swap(msg.header.keylen);
1815
+ std::uint16_t key_size = utils::network_to_host(msg.header.keylen);
1821
1816
  if (magic == protocol::magic::alt_client_response) {
1822
- framing_extras_size = static_cast<std::uint8_t>(msg.header.keylen >> 8U);
1823
- key_size = msg.header.keylen & 0xffU;
1817
+ const auto sizes = msg.header.alt_sizes();
1818
+ framing_extras_size = sizes.framing_extras;
1819
+ key_size = sizes.key;
1824
1820
  }
1825
1821
 
1826
1822
  const std::vector<std::uint8_t>::difference_type offset =
1827
1823
  framing_extras_size + key_size + extras_size;
1828
- if (utils::byte_swap(msg.header.bodylen) - offset > 0) {
1829
- std::string_view config_text{ reinterpret_cast<const char*>(msg.body.data()) + offset,
1830
- msg.body.size() - static_cast<std::size_t>(offset) };
1824
+ if (utils::network_to_host(msg.header.bodylen) - offset > 0) {
1825
+ const std::string_view config_text{ reinterpret_cast<const char*>(msg.body.data()) + offset,
1826
+ msg.body.size() - static_cast<std::size_t>(offset) };
1831
1827
  if (origin_.options().dump_configuration) {
1832
1828
  CB_LOG_TRACE(
1833
- "{} configuration from not_my_vbucket response (size={}, endpoint=\"{}:{}\"), {}",
1829
+ "{} configuration from not_my_vbucket response (size={}, endpoint=\"{}\"), {}",
1834
1830
  log_prefix_,
1835
1831
  config_text.size(),
1836
- bootstrap_hostname_,
1837
- bootstrap_port_number_,
1838
- config_text);
1832
+ logger::system_data(fmt::format("{}:{}", bootstrap_hostname_, bootstrap_port_number_)),
1833
+ // an opt-in dump, it exists to show the exact bytes. That is the
1834
+ // "none at all" case of the rule in redaction.hxx.
1835
+ logger::not_redacted(config_text));
1839
1836
  }
1840
1837
  auto config =
1841
1838
  protocol::parse_config(config_text, bootstrap_hostname_, bootstrap_port_number_);
@@ -1843,7 +1840,7 @@ public:
1843
1840
  "{} received not_my_vbucket status for {}, opaque={} with config rev={} in the payload",
1844
1841
  log_prefix_,
1845
1842
  protocol::client_opcode(msg.header.opcode),
1846
- utils::byte_swap(msg.header.opaque),
1843
+ utils::network_to_host(msg.header.opaque),
1847
1844
  config.rev_str());
1848
1845
  update_configuration(std::move(config));
1849
1846
  }
@@ -1863,15 +1860,19 @@ public:
1863
1860
  collection_cache_.update(path, uid);
1864
1861
  }
1865
1862
 
1866
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
1867
- void add_background_bootstrap_listener(
1868
- std::shared_ptr<columnar::background_bootstrap_listener> listener)
1863
+ private:
1864
+ /**
1865
+ * The bucket name for the log prefix, or an untagged "-" when the session is not bound to a
1866
+ * bucket.
1867
+ */
1868
+ [[nodiscard]] auto log_prefix_bucket_name() const -> std::string
1869
1869
  {
1870
- background_bootstrap_listener_ = std::move(listener);
1870
+ if (!bucket_name_.has_value()) {
1871
+ return "-";
1872
+ }
1873
+ return fmt::format("{}", logger::metadata(bucket_name_.value()));
1871
1874
  }
1872
- #endif
1873
1875
 
1874
- private:
1875
1876
  void invoke_bootstrap_handler(std::error_code ec)
1876
1877
  {
1877
1878
  connection_deadline_.cancel();
@@ -1961,10 +1962,9 @@ private:
1961
1962
  return initiate_bootstrap();
1962
1963
  }
1963
1964
  endpoints_ = endpoints;
1964
- CB_LOG_TRACE("{} resolved \"{}:{}\" to {} endpoint(s)",
1965
+ CB_LOG_TRACE("{} resolved \"{}\" to {} endpoint(s)",
1965
1966
  log_prefix_,
1966
- bootstrap_hostname_,
1967
- bootstrap_port_,
1967
+ logger::system_data(bootstrap_address_),
1968
1968
  endpoints_.size());
1969
1969
  do_connect(endpoints_.begin());
1970
1970
  }
@@ -1978,12 +1978,10 @@ private:
1978
1978
  if (it != endpoints_.end()) {
1979
1979
  auto hostname = it->endpoint().address().to_string();
1980
1980
  auto port = it->endpoint().port();
1981
- CB_LOG_DEBUG("{} connecting to {}:{} (\"{}:{}\"), timeout={}ms",
1981
+ CB_LOG_DEBUG("{} connecting to {} (\"{}\"), timeout={}ms",
1982
1982
  log_prefix_,
1983
- hostname,
1984
- port,
1985
- bootstrap_hostname_,
1986
- bootstrap_port_,
1983
+ logger::system_data(fmt::format("{}:{}", hostname, port)),
1984
+ logger::system_data(bootstrap_address_),
1987
1985
  origin_.options().connect_timeout.count());
1988
1986
  connection_deadline_.expires_after(origin_.options().connect_timeout);
1989
1987
  connection_deadline_.async_wait(
@@ -1991,17 +1989,16 @@ private:
1991
1989
  if (timer_ec == asio::error::operation_aborted || self->stopped_) {
1992
1990
  return;
1993
1991
  }
1994
- CB_LOG_DEBUG("{} unable to connect to {}:{} (\"{}:{}\") in time, reconnecting",
1992
+ CB_LOG_DEBUG("{} unable to connect to {} (\"{}\") in time, reconnecting",
1995
1993
  self->log_prefix_,
1996
- hostname,
1997
- port,
1998
- self->bootstrap_hostname_,
1999
- self->bootstrap_port_);
1994
+ logger::system_data(fmt::format("{}:{}", hostname, port)),
1995
+ logger::system_data(self->bootstrap_address_));
2000
1996
  self->initiate_bootstrap();
2001
1997
  });
2002
- stream_->async_connect(it->endpoint(), [capture0 = shared_from_this(), it](auto&& PH1) {
2003
- capture0->on_connect(std::forward<decltype(PH1)>(PH1), it);
2004
- });
1998
+ stream_->async_connect(
1999
+ it->endpoint(), bootstrap_hostname_, [self = shared_from_this(), it](auto&& ec) {
2000
+ self->on_connect(std::forward<decltype(ec)>(ec), it);
2001
+ });
2005
2002
  } else {
2006
2003
  auto error_msg =
2007
2004
  fmt::format("no more endpoints left to connect to \"{}:{}\", will try another address",
@@ -2037,10 +2034,10 @@ private:
2037
2034
  ? ERR_error_string(static_cast<unsigned long>(ec.value()), nullptr)
2038
2035
  : ec.message();
2039
2036
  #endif
2040
- CB_LOG_WARNING("{} unable to connect to {}:{}: {} ({}){}. is_open={}",
2037
+ CB_LOG_WARNING("{} unable to connect to {}: {} ({}){}. is_open={}",
2041
2038
  log_prefix_,
2042
- it->endpoint().address().to_string(),
2043
- it->endpoint().port(),
2039
+ logger::system_data(fmt::format(
2040
+ "{}:{}", it->endpoint().address().to_string(), it->endpoint().port())),
2044
2041
  ec.value(),
2045
2042
  error_message,
2046
2043
  (ec == asio::error::connection_refused)
@@ -2051,10 +2048,11 @@ private:
2051
2048
  stream_->close([self = shared_from_this(), next_address = ++it](std::error_code ec) {
2052
2049
  if (ec) {
2053
2050
  CB_LOG_WARNING(
2054
- "{} unable to close socket, but continue connecting attempt to {}:{}: {}",
2051
+ "{} unable to close socket, but continue connecting attempt to {}: {}",
2055
2052
  self->log_prefix_,
2056
- next_address->endpoint().address().to_string(),
2057
- next_address->endpoint().port(),
2053
+ logger::system_data(fmt::format("{}:{}",
2054
+ next_address->endpoint().address().to_string(),
2055
+ next_address->endpoint().port())),
2058
2056
  ec.value());
2059
2057
  }
2060
2058
  self->do_connect(next_address);
@@ -2065,24 +2063,31 @@ private:
2065
2063
  } else {
2066
2064
  stream_->set_options();
2067
2065
  connection_endpoints_ = { it->endpoint(), stream_->local_endpoint() };
2068
- CB_LOG_DEBUG("{} connected to {}:{}:{}",
2066
+ CB_LOG_DEBUG("{} connected to {}",
2069
2067
  log_prefix_,
2070
- connection_endpoints_.local.port(),
2071
- connection_endpoints_.remote_address,
2072
- connection_endpoints_.remote.port());
2073
- log_prefix_ = fmt::format("[{}/{}/{}/{}] <{}:{}/{}:{}>",
2074
- client_id_,
2075
- id_,
2076
- stream_->log_prefix(),
2077
- bucket_name_.value_or("-"),
2078
- connection_endpoints_.local.port(),
2079
- bootstrap_hostname_,
2080
- connection_endpoints_.remote_address,
2081
- connection_endpoints_.remote.port());
2082
- parser_.reset();
2068
+ logger::system_data(fmt::format("{}:{}:{}",
2069
+ connection_endpoints_.local.port(),
2070
+ connection_endpoints_.remote_address,
2071
+ connection_endpoints_.remote.port())));
2083
2072
  {
2084
- const std::scoped_lock lock(output_buffer_mutex_);
2085
- output_buffer_.clear();
2073
+ const std::scoped_lock lock(session_info_mutex_);
2074
+ const auto endpoints = fmt::format("{}:{}/{}:{}",
2075
+ connection_endpoints_.local.port(),
2076
+ bootstrap_hostname_,
2077
+ connection_endpoints_.remote_address,
2078
+ connection_endpoints_.remote.port());
2079
+ log_prefix_ = fmt::format("[{}/{}/{}/{}] <{}>",
2080
+ client_id_,
2081
+ id_,
2082
+ stream_->log_prefix(),
2083
+ log_prefix_bucket_name(),
2084
+ logger::system_data(endpoints));
2085
+ }
2086
+ parser_.reset();
2087
+ output_queue_.reset();
2088
+ // cppcheck-suppress knownConditionTrueFalse
2089
+ if (stopped_) {
2090
+ return;
2086
2091
  }
2087
2092
  bootstrap_handler_ = std::make_shared<bootstrap_handler>(shared_from_this());
2088
2093
 
@@ -2091,14 +2096,13 @@ private:
2091
2096
  if (timer_ec == asio::error::operation_aborted || self->stopped_) {
2092
2097
  return;
2093
2098
  }
2094
- CB_LOG_DEBUG(
2095
- "{} unable to boostrap single node at {}:{}:{} (\"{}:{}\") in time, reconnecting",
2096
- self->log_prefix_,
2097
- self->connection_endpoints_.local.port(),
2098
- self->connection_endpoints_.remote_address,
2099
- self->connection_endpoints_.remote.port(),
2100
- self->bootstrap_hostname_,
2101
- self->bootstrap_port_);
2099
+ CB_LOG_DEBUG("{} unable to bootstrap single node at {} (\"{}\") in time, reconnecting",
2100
+ self->log_prefix_,
2101
+ logger::system_data(fmt::format("{}:{}:{}",
2102
+ self->connection_endpoints_.local.port(),
2103
+ self->connection_endpoints_.remote_address,
2104
+ self->connection_endpoints_.remote.port())),
2105
+ logger::system_data(self->bootstrap_address_));
2102
2106
  return self->initiate_bootstrap();
2103
2107
  });
2104
2108
  }
@@ -2160,6 +2164,10 @@ private:
2160
2164
 
2161
2165
  for (;;) {
2162
2166
  mcbp_message msg{};
2167
+ // Start from a recycled buffer (if any) and tag the message with the pool so the frame
2168
+ // buffer is returned when the decoded response is destroyed on this IO thread.
2169
+ msg.body = tls_response_body_pool().acquire();
2170
+ msg.recycle_body = true;
2163
2171
  switch (self->parser_.next(msg)) {
2164
2172
  case mcbp_parser::result::ok: {
2165
2173
  if (self->stopped_) {
@@ -2167,16 +2175,26 @@ private:
2167
2175
  }
2168
2176
  CB_LOG_TRACE(
2169
2177
  "{} MCBP recv {}", self->log_prefix_, mcbp_header_view(msg.header_data()));
2178
+ // Copy the handler's shared_ptr into a local before dispatching.
2179
+ // handle() can trigger a nested stop() (e.g. reauth failure) that
2180
+ // releases the session's own strong ref to the handler; the local
2181
+ // copy keeps it alive for the duration of the call so it is not
2182
+ // freed mid-dispatch (CXXCBC-842).
2170
2183
  if (self->bootstrapped_) {
2171
- self->handler_->handle(std::move(msg));
2172
- } else if (self->bootstrap_handler_) {
2173
- self->bootstrap_handler_->handle(std::move(msg));
2184
+ if (auto h = self->handler_; h) {
2185
+ h->handle(std::move(msg));
2186
+ }
2187
+ } else if (auto h = self->bootstrap_handler_; h) {
2188
+ h->handle(std::move(msg));
2174
2189
  }
2175
2190
  if (self->stopped_) {
2176
2191
  return;
2177
2192
  }
2178
2193
  } break;
2179
2194
  case mcbp_parser::result::need_data:
2195
+ // The frame is incomplete, so next() did not consume the buffer we handed it; return
2196
+ // it to the pool instead of letting this partial-read iteration drain the pool.
2197
+ tls_response_body_pool().release(std::move(msg.body));
2180
2198
  self->reading_ = false;
2181
2199
  if (!self->stopped_ && self->stream_->is_open()) {
2182
2200
  self->do_read();
@@ -2194,14 +2212,12 @@ private:
2194
2212
  if (stopped_ || !stream_->is_open()) {
2195
2213
  return;
2196
2214
  }
2197
- const std::scoped_lock lock(writing_buffer_mutex_, output_buffer_mutex_);
2198
- if (!writing_buffer_.empty() || output_buffer_.empty()) {
2215
+ if (!output_queue_.begin_writing()) {
2199
2216
  return;
2200
2217
  }
2201
- std::swap(writing_buffer_, output_buffer_);
2202
2218
  std::vector<asio::const_buffer> buffers;
2203
- buffers.reserve(writing_buffer_.size());
2204
- for (auto& buf : writing_buffer_) {
2219
+ buffers.reserve(output_queue_.writing().size());
2220
+ for (const auto& buf : output_queue_.writing()) {
2205
2221
  CB_LOG_PROTOCOL("[MCBP, OUT] host=\"{}\", sport={}, dport={}, buffer_size={}{:a}",
2206
2222
  connection_endpoints_.remote_address,
2207
2223
  connection_endpoints_.local.port(),
@@ -2231,10 +2247,7 @@ private:
2231
2247
  ec.message());
2232
2248
  return self->stop(retry_reason::socket_closed_while_in_flight);
2233
2249
  }
2234
- {
2235
- const std::scoped_lock inner_lock(self->writing_buffer_mutex_);
2236
- self->writing_buffer_.clear();
2237
- }
2250
+ self->output_queue_.finish_writing();
2238
2251
  asio::post(asio::bind_executor(self->ctx_, [self]() {
2239
2252
  self->do_write();
2240
2253
  self->do_read();
@@ -2244,6 +2257,7 @@ private:
2244
2257
 
2245
2258
  const std::string client_id_;
2246
2259
  std::string node_uuid_;
2260
+ app_telemetry_recorder_cache app_telemetry_recorder_cache_{};
2247
2261
  const std::string id_{ uuid::to_string(uuid::random()) };
2248
2262
  asio::io_context& ctx_;
2249
2263
  asio::ip::tcp::resolver resolver_;
@@ -2263,7 +2277,7 @@ private:
2263
2277
  utils::movable_function<void(std::error_code, const topology::configuration&)>
2264
2278
  bootstrap_callback_{};
2265
2279
  std::mutex command_handlers_mutex_{};
2266
- std::map<std::uint32_t, command_handler> command_handlers_{};
2280
+ opaque_ring_table<command_handler> command_handlers_{};
2267
2281
  std::vector<std::shared_ptr<config_listener>> config_listeners_{};
2268
2282
  utils::movable_function<void()> on_stop_handler_{};
2269
2283
 
@@ -2278,18 +2292,16 @@ private:
2278
2292
  std::atomic<std::uint32_t> opaque_{ 0 };
2279
2293
 
2280
2294
  std::array<std::byte, 16384> input_buffer_{};
2281
- std::vector<std::vector<std::byte>> output_buffer_{};
2295
+ mcbp_output_queue output_queue_{};
2282
2296
  std::vector<std::vector<std::byte>> pending_buffer_{};
2283
- std::vector<std::vector<std::byte>> writing_buffer_{};
2284
- std::mutex output_buffer_mutex_{};
2285
2297
  std::mutex pending_buffer_mutex_{};
2286
- std::mutex writing_buffer_mutex_{};
2287
2298
  std::string bootstrap_hostname_{};
2288
2299
  std::string bootstrap_port_{};
2289
2300
  std::string bootstrap_address_{};
2290
2301
  std::uint16_t bootstrap_port_number_{};
2291
2302
  connection_endpoints connection_endpoints_{ {}, {} };
2292
2303
  asio::ip::tcp::resolver::results_type endpoints_;
2304
+ mutable std::mutex session_info_mutex_{};
2293
2305
  std::vector<protocol::hello_feature> supported_features_;
2294
2306
  std::optional<topology::configuration> config_;
2295
2307
  mutable std::mutex config_mutex_{};
@@ -2315,11 +2327,6 @@ private:
2315
2327
  std::string log_prefix_{};
2316
2328
  std::chrono::time_point<std::chrono::steady_clock> last_active_{};
2317
2329
  std::atomic<diag::endpoint_state> state_{ diag::endpoint_state::disconnected };
2318
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
2319
- std::shared_ptr<columnar::background_bootstrap_listener> background_bootstrap_listener_{
2320
- nullptr
2321
- };
2322
- #endif
2323
2330
  };
2324
2331
 
2325
2332
  mcbp_session::mcbp_session(const std::string& client_id,
@@ -2358,8 +2365,13 @@ mcbp_session::mcbp_session(const std::string& client_id,
2358
2365
  {
2359
2366
  }
2360
2367
 
2368
+ mcbp_session::mcbp_session(std::shared_ptr<mcbp_session_impl> impl)
2369
+ : impl_{ std::move(impl) }
2370
+ {
2371
+ }
2372
+
2361
2373
  auto
2362
- mcbp_session::log_prefix() const -> const std::string&
2374
+ mcbp_session::log_prefix() const -> std::string
2363
2375
  {
2364
2376
  return impl_->log_prefix();
2365
2377
  }
@@ -2418,6 +2430,14 @@ mcbp_session::node_uuid() const -> const std::string&
2418
2430
  return impl_->node_uuid();
2419
2431
  }
2420
2432
 
2433
+ auto
2434
+ mcbp_session::app_telemetry_recorder(app_telemetry_meter& meter,
2435
+ const std::string& bucket_name) const
2436
+ -> std::shared_ptr<app_telemetry_value_recorder>
2437
+ {
2438
+ return impl_->app_telemetry_recorder(meter, bucket_name);
2439
+ }
2440
+
2421
2441
  auto
2422
2442
  mcbp_session::bootstrap_address() const -> const std::string&
2423
2443
  {
@@ -2466,18 +2486,6 @@ mcbp_session::bootstrap_port_number() const -> std::uint16_t
2466
2486
  return impl_->bootstrap_port_number();
2467
2487
  }
2468
2488
 
2469
- auto
2470
- mcbp_session::last_bootstrap_error() && -> std::optional<impl::bootstrap_error>
2471
- {
2472
- return std::move(impl_)->last_bootstrap_error();
2473
- }
2474
-
2475
- auto
2476
- mcbp_session::last_bootstrap_error() const& -> const std::optional<impl::bootstrap_error>&
2477
- {
2478
- return impl_->last_bootstrap_error();
2479
- }
2480
-
2481
2489
  auto
2482
2490
  mcbp_session::canonical_hostname() const -> const std::string&
2483
2491
  {
@@ -2610,13 +2618,4 @@ mcbp_session::write_and_flush(std::vector<std::byte>&& buffer)
2610
2618
  return impl_->write_and_flush(std::move(buffer));
2611
2619
  }
2612
2620
 
2613
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
2614
- void
2615
- mcbp_session::add_background_bootstrap_listener(
2616
- std::shared_ptr<columnar::background_bootstrap_listener> listener)
2617
- {
2618
- return impl_->add_background_bootstrap_listener(std::move(listener));
2619
- }
2620
- #endif
2621
-
2622
2621
  } // namespace couchbase::core::io