couchbase 3.8.2 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (473) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/ext/CMakeLists.txt +16 -1
  4. data/ext/cache/cpm/{CPM_0.42.0.cmake → CPM_0.43.1.cmake} +18 -2
  5. data/ext/cache/extconf_include.rb +6 -6
  6. data/ext/cache/mozilla-ca-bundle.crt +121 -55
  7. data/ext/cache/mozilla-ca-bundle.sha256 +1 -1
  8. data/ext/couchbase/CMakeLists.txt +130 -21
  9. data/ext/couchbase/cmake/APKBUILD.in +25 -4
  10. data/ext/couchbase/cmake/Bundler.cmake +143 -34
  11. data/ext/couchbase/cmake/CPM.cmake +126 -12
  12. data/ext/couchbase/cmake/Cache.cmake +7 -1
  13. data/ext/couchbase/cmake/CompilerOptions.cmake +1 -1
  14. data/ext/couchbase/cmake/CompilerWarnings.cmake +17 -6
  15. data/ext/couchbase/cmake/GrpcProtobuf.cmake +481 -0
  16. data/ext/couchbase/cmake/OpenSSL.cmake +10 -3
  17. data/ext/couchbase/cmake/Packaging.cmake +519 -65
  18. data/ext/couchbase/cmake/Protostellar.cmake +250 -0
  19. data/ext/couchbase/cmake/Sanitizers.cmake +61 -50
  20. data/ext/couchbase/cmake/StaticAnalyzers.cmake +16 -5
  21. data/ext/couchbase/cmake/TestFramework.cmake +283 -0
  22. data/ext/couchbase/cmake/TestFrameworkAddTests.cmake +51 -0
  23. data/ext/couchbase/cmake/Testing.cmake +27 -9
  24. data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +141 -8
  25. data/ext/couchbase/cmake/VersionInfo.cmake +50 -0
  26. data/ext/couchbase/cmake/build_config.hxx.in +2 -1
  27. data/ext/couchbase/cmake/couchbase-cxx-client.spec.in +101 -23
  28. data/ext/couchbase/cmake/couchbase_cxx_client_static-config.cmake.in +7 -0
  29. data/ext/couchbase/cmake/couchbase_cxx_client_static.pc.in +2 -0
  30. data/ext/couchbase/cmake/gRPC_Generate.cmake +58 -0
  31. data/ext/couchbase/cmake/protobuf_Generate.cmake +69 -0
  32. data/ext/couchbase/cmake/require-git-checkout.cmake +25 -0
  33. data/ext/couchbase/cmake/tarball_glob.txt +49 -0
  34. data/ext/couchbase/core/agent_group.cxx +0 -8
  35. data/ext/couchbase/core/agent_group.hxx +0 -4
  36. data/ext/couchbase/core/analytics_stream.cxx +314 -0
  37. data/ext/couchbase/core/analytics_stream.hxx +129 -0
  38. data/ext/couchbase/core/analytics_stream_component.cxx +245 -0
  39. data/ext/couchbase/core/analytics_stream_component.hxx +67 -0
  40. data/ext/couchbase/core/app_telemetry_meter.cxx +25 -3
  41. data/ext/couchbase/core/app_telemetry_meter.hxx +77 -0
  42. data/ext/couchbase/core/app_telemetry_reporter.cxx +7 -6
  43. data/ext/couchbase/core/bucket.cxx +381 -87
  44. data/ext/couchbase/core/bucket.hxx +105 -25
  45. data/ext/couchbase/core/bucket_unit_test_api.hxx +57 -0
  46. data/ext/couchbase/core/capella.hxx +91 -0
  47. data/ext/couchbase/core/cluster.cxx +1554 -398
  48. data/ext/couchbase/core/cluster.hxx +39 -8
  49. data/ext/couchbase/core/cluster_agent.cxx +0 -8
  50. data/ext/couchbase/core/cluster_agent.hxx +0 -4
  51. data/ext/couchbase/core/cluster_options.hxx +26 -3
  52. data/ext/couchbase/core/collections_component.cxx +39 -15
  53. data/ext/couchbase/core/config_profiles.cxx +1 -1
  54. data/ext/couchbase/core/crud_component.cxx +2 -2
  55. data/ext/couchbase/core/crud_options.hxx +34 -0
  56. data/ext/couchbase/core/crypto/cbcrypto.cc +18 -13
  57. data/ext/couchbase/core/document_id_redaction.hxx +81 -0
  58. data/ext/couchbase/core/error_context/base_error_context.hxx +7 -6
  59. data/ext/couchbase/core/error_context/key_value.cxx +25 -2
  60. data/ext/couchbase/core/error_context/key_value.hxx +60 -33
  61. data/ext/couchbase/core/error_context/key_value_error_context.hxx +16 -15
  62. data/ext/couchbase/core/error_context/search.hxx +5 -0
  63. data/ext/couchbase/core/error_context/search_json.hxx +3 -0
  64. data/ext/couchbase/core/error_context/subdocument_error_context.hxx +19 -18
  65. data/ext/couchbase/core/error_context/view.hxx +5 -0
  66. data/ext/couchbase/core/free_form_http_request.cxx +237 -4
  67. data/ext/couchbase/core/free_form_http_request.hxx +41 -8
  68. data/ext/couchbase/core/http_component.cxx +300 -284
  69. data/ext/couchbase/core/http_component.hxx +0 -4
  70. data/ext/couchbase/core/impl/analytics.cxx +38 -5
  71. data/ext/couchbase/core/impl/analytics.hxx +34 -0
  72. data/ext/couchbase/core/impl/bootstrap_error.hxx +0 -3
  73. data/ext/couchbase/core/impl/collection.cxx +215 -202
  74. data/ext/couchbase/core/impl/collection_manager.cxx +1 -1
  75. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +45 -18
  76. data/ext/couchbase/core/impl/error.cxx +22 -2
  77. data/ext/couchbase/core/impl/internal_analytics_stream_result.hxx +111 -0
  78. data/ext/couchbase/core/impl/internal_query_stream_result.hxx +110 -0
  79. data/ext/couchbase/core/impl/invoke_with_node_id.hxx +15 -7
  80. data/ext/couchbase/core/impl/key_value_error_category.cxx +6 -0
  81. data/ext/couchbase/core/impl/lookup_in_replica.cxx +10 -10
  82. data/ext/couchbase/core/impl/lookup_in_replica.hxx +4 -4
  83. data/ext/couchbase/core/impl/node_id.cxx +49 -13
  84. data/ext/couchbase/core/impl/observability_recorder.cxx +12 -2
  85. data/ext/couchbase/core/impl/observe_poll.cxx +63 -23
  86. data/ext/couchbase/core/impl/public_analytics_stream_result.cxx +372 -0
  87. data/ext/couchbase/core/impl/public_bucket.cxx +48 -2
  88. data/ext/couchbase/core/impl/public_cluster.cxx +229 -42
  89. data/ext/couchbase/core/impl/public_query_stream_result.cxx +380 -0
  90. data/ext/couchbase/core/impl/public_transaction_get_multi_replicas_from_preferred_server_group_result.cxx +42 -0
  91. data/ext/couchbase/core/impl/public_transaction_get_multi_result.cxx +42 -0
  92. data/ext/couchbase/core/impl/query.cxx +76 -7
  93. data/ext/couchbase/core/impl/query.hxx +36 -0
  94. data/ext/couchbase/core/impl/query_index_manager.cxx +54 -1
  95. data/ext/couchbase/core/impl/replica_utils.cxx +101 -0
  96. data/ext/couchbase/core/impl/replica_utils.hxx +64 -0
  97. data/ext/couchbase/core/impl/resolve_node_id.hxx +125 -0
  98. data/ext/couchbase/core/impl/scope.cxx +70 -0
  99. data/ext/couchbase/core/impl/search.cxx +31 -1
  100. data/ext/couchbase/core/impl/transactions.cxx +130 -0
  101. data/ext/couchbase/core/impl/wait_until_ready.cxx +439 -0
  102. data/ext/couchbase/core/impl/wait_until_ready.hxx +110 -0
  103. data/ext/couchbase/core/impl/with_bucket_config_or_timeout.hxx +180 -0
  104. data/ext/couchbase/core/impl/with_cancellation.hxx +11 -3
  105. data/ext/couchbase/core/io/configuration_belongs_to_session.hxx +1 -2
  106. data/ext/couchbase/core/io/dns_client.cxx +122 -45
  107. data/ext/couchbase/core/io/dns_codec.hxx +21 -21
  108. data/ext/couchbase/core/io/dns_config.cxx +21 -6
  109. data/ext/couchbase/core/io/http_command.hxx +149 -121
  110. data/ext/couchbase/core/io/http_error.hxx +107 -0
  111. data/ext/couchbase/core/io/http_parser.cxx +4 -0
  112. data/ext/couchbase/core/io/http_session.cxx +606 -154
  113. data/ext/couchbase/core/io/http_session.hxx +147 -30
  114. data/ext/couchbase/core/io/http_session_manager.hxx +329 -382
  115. data/ext/couchbase/core/io/http_streaming_parser.cxx +4 -0
  116. data/ext/couchbase/core/io/http_streaming_response.cxx +328 -43
  117. data/ext/couchbase/core/io/http_streaming_response.hxx +37 -3
  118. data/ext/couchbase/core/io/mcbp_buffer_pool.hxx +122 -0
  119. data/ext/couchbase/core/io/mcbp_command.hxx +150 -51
  120. data/ext/couchbase/core/io/mcbp_command_id.hxx +45 -0
  121. data/ext/couchbase/core/io/mcbp_message.cxx +9 -1
  122. data/ext/couchbase/core/io/mcbp_message.hxx +19 -0
  123. data/ext/couchbase/core/io/mcbp_output_queue.hxx +150 -0
  124. data/ext/couchbase/core/io/mcbp_parser.cxx +24 -7
  125. data/ext/couchbase/core/io/mcbp_session.cxx +309 -301
  126. data/ext/couchbase/core/io/mcbp_session.hxx +27 -18
  127. data/ext/couchbase/core/io/opaque_ring_table.hxx +145 -0
  128. data/ext/couchbase/core/io/retry_orchestrator.hxx +6 -5
  129. data/ext/couchbase/core/io/stream_deadline.hxx +50 -0
  130. data/ext/couchbase/core/io/streams.cxx +151 -21
  131. data/ext/couchbase/core/io/streams.hxx +48 -1
  132. data/ext/couchbase/core/logger/logger.cxx +36 -0
  133. data/ext/couchbase/core/logger/logger.hxx +46 -6
  134. data/ext/couchbase/core/logger/redaction.hxx +503 -0
  135. data/ext/couchbase/core/management/eventing_status.hxx +10 -0
  136. data/ext/couchbase/core/management/eventing_status_json.hxx +4 -0
  137. data/ext/couchbase/core/mcbp/big_endian.cxx +1 -1
  138. data/ext/couchbase/core/mcbp/codec.cxx +27 -18
  139. data/ext/couchbase/core/mcbp/operation_queue.cxx +2 -2
  140. data/ext/couchbase/core/mcbp/packet.hxx +2 -2
  141. data/ext/couchbase/core/mcbp/queue_request.cxx +23 -6
  142. data/ext/couchbase/core/mcbp/queue_request.hxx +21 -0
  143. data/ext/couchbase/core/meta/features.hxx +13 -0
  144. data/ext/couchbase/core/meta/version.cxx +6 -3
  145. data/ext/couchbase/core/metrics/logging_meter.cxx +5 -10
  146. data/ext/couchbase/core/metrics/meter_wrapper.cxx +63 -33
  147. data/ext/couchbase/core/metrics/meter_wrapper.hxx +55 -3
  148. data/ext/couchbase/core/operations/analytics_response_parsing.cxx +177 -0
  149. data/ext/couchbase/core/operations/analytics_response_parsing.hxx +55 -0
  150. data/ext/couchbase/core/operations/document_analytics.cxx +48 -130
  151. data/ext/couchbase/core/operations/document_analytics.hxx +14 -2
  152. data/ext/couchbase/core/operations/document_get_all_replicas.hxx +15 -7
  153. data/ext/couchbase/core/operations/document_get_any_replica.hxx +19 -8
  154. data/ext/couchbase/core/operations/document_get_projected.cxx +1 -1
  155. data/ext/couchbase/core/{impl/get_replica.cxx → operations/document_get_replica.cxx} +21 -3
  156. data/ext/couchbase/core/{impl/get_replica.hxx → operations/document_get_replica.hxx} +31 -4
  157. data/ext/couchbase/core/operations/document_lookup_in.cxx +10 -10
  158. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +228 -198
  159. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +215 -217
  160. data/ext/couchbase/core/operations/document_query.cxx +108 -221
  161. data/ext/couchbase/core/operations/document_query.hxx +18 -4
  162. data/ext/couchbase/core/operations/document_search.cxx +52 -8
  163. data/ext/couchbase/core/operations/document_search.hxx +3 -0
  164. data/ext/couchbase/core/operations/document_view.cxx +3 -3
  165. data/ext/couchbase/core/operations/management/analytics_dataset_create.cxx +15 -8
  166. data/ext/couchbase/core/operations/management/analytics_dataset_create.hxx +4 -0
  167. data/ext/couchbase/core/operations/management/analytics_dataset_drop.cxx +11 -6
  168. data/ext/couchbase/core/operations/management/analytics_dataset_get_all.cxx +3 -2
  169. data/ext/couchbase/core/operations/management/analytics_dataverse_create.cxx +10 -5
  170. data/ext/couchbase/core/operations/management/analytics_dataverse_drop.cxx +10 -5
  171. data/ext/couchbase/core/operations/management/analytics_get_pending_mutations.cxx +2 -1
  172. data/ext/couchbase/core/operations/management/analytics_index_create.cxx +33 -8
  173. data/ext/couchbase/core/operations/management/analytics_index_drop.cxx +12 -7
  174. data/ext/couchbase/core/operations/management/analytics_index_get_all.cxx +3 -2
  175. data/ext/couchbase/core/operations/management/analytics_link_connect.cxx +11 -6
  176. data/ext/couchbase/core/operations/management/analytics_link_create.cxx +3 -3
  177. data/ext/couchbase/core/operations/management/analytics_link_disconnect.cxx +10 -4
  178. data/ext/couchbase/core/operations/management/analytics_link_drop.cxx +6 -4
  179. data/ext/couchbase/core/operations/management/analytics_link_get_all.cxx +4 -3
  180. data/ext/couchbase/core/operations/management/analytics_link_replace.cxx +3 -3
  181. data/ext/couchbase/core/operations/management/analytics_link_utils.hxx +1 -1
  182. data/ext/couchbase/core/operations/management/change_password.cxx +2 -0
  183. data/ext/couchbase/core/operations/management/collection_drop.cxx +5 -1
  184. data/ext/couchbase/core/operations/management/error_utils.cxx +59 -5
  185. data/ext/couchbase/core/operations/management/error_utils.hxx +29 -1
  186. data/ext/couchbase/core/operations/management/eventing_deploy_function.cxx +9 -3
  187. data/ext/couchbase/core/operations/management/eventing_drop_function.cxx +8 -3
  188. data/ext/couchbase/core/operations/management/eventing_get_all_functions.cxx +4 -2
  189. data/ext/couchbase/core/operations/management/eventing_get_function.cxx +5 -3
  190. data/ext/couchbase/core/operations/management/eventing_get_status.cxx +4 -2
  191. data/ext/couchbase/core/operations/management/eventing_pause_function.cxx +9 -3
  192. data/ext/couchbase/core/operations/management/eventing_resume_function.cxx +9 -3
  193. data/ext/couchbase/core/operations/management/eventing_undeploy_function.cxx +9 -3
  194. data/ext/couchbase/core/operations/management/eventing_upsert_function.cxx +9 -3
  195. data/ext/couchbase/core/operations/management/group_drop.cxx +3 -1
  196. data/ext/couchbase/core/operations/management/group_get.cxx +3 -1
  197. data/ext/couchbase/core/operations/management/group_upsert.cxx +11 -8
  198. data/ext/couchbase/core/operations/management/query_index_build.cxx +5 -2
  199. data/ext/couchbase/core/operations/management/query_index_create.cxx +9 -3
  200. data/ext/couchbase/core/operations/management/query_index_drop.cxx +11 -4
  201. data/ext/couchbase/core/operations/management/query_index_get_all.cxx +1 -1
  202. data/ext/couchbase/core/operations/management/query_index_get_all_deferred.cxx +1 -1
  203. data/ext/couchbase/core/operations/management/scope_drop.cxx +5 -1
  204. data/ext/couchbase/core/operations/management/scope_get_all.cxx +6 -1
  205. data/ext/couchbase/core/operations/management/search_get_stats.cxx +10 -0
  206. data/ext/couchbase/core/operations/management/search_index_analyze_document.cxx +3 -2
  207. data/ext/couchbase/core/operations/management/search_index_control_ingest.cxx +4 -3
  208. data/ext/couchbase/core/operations/management/search_index_control_plan_freeze.cxx +4 -3
  209. data/ext/couchbase/core/operations/management/search_index_control_query.cxx +4 -3
  210. data/ext/couchbase/core/operations/management/search_index_drop.cxx +2 -2
  211. data/ext/couchbase/core/operations/management/search_index_get.cxx +2 -2
  212. data/ext/couchbase/core/operations/management/search_index_get_documents_count.cxx +3 -2
  213. data/ext/couchbase/core/operations/management/search_index_get_stats.cxx +3 -1
  214. data/ext/couchbase/core/operations/management/search_index_upsert.cxx +2 -2
  215. data/ext/couchbase/core/operations/management/user_drop.cxx +3 -1
  216. data/ext/couchbase/core/operations/management/user_get.cxx +3 -1
  217. data/ext/couchbase/core/operations/management/user_upsert.cxx +12 -9
  218. data/ext/couchbase/core/operations/management/view_index_drop.cxx +1 -1
  219. data/ext/couchbase/core/operations/management/view_index_get.cxx +1 -1
  220. data/ext/couchbase/core/operations/management/view_index_upsert.cxx +1 -1
  221. data/ext/couchbase/core/operations/operation_traits.hxx +18 -0
  222. data/ext/couchbase/core/operations/query_response_parsing.cxx +211 -0
  223. data/ext/couchbase/core/operations/query_response_parsing.hxx +60 -0
  224. data/ext/couchbase/core/operations_fwd.hxx +3 -3
  225. data/ext/couchbase/core/origin.cxx +54 -26
  226. data/ext/couchbase/core/origin.hxx +12 -0
  227. data/ext/couchbase/core/orphan_reporter.cxx +2 -1
  228. data/ext/couchbase/core/platform/random.cc +7 -7
  229. data/ext/couchbase/core/protocol/client_request.cxx +2 -2
  230. data/ext/couchbase/core/protocol/client_request.hxx +38 -21
  231. data/ext/couchbase/core/protocol/client_response.cxx +11 -3
  232. data/ext/couchbase/core/protocol/client_response.hxx +27 -6
  233. data/ext/couchbase/core/protocol/cmd_append.cxx +2 -2
  234. data/ext/couchbase/core/protocol/cmd_cluster_map_change_notification.cxx +2 -2
  235. data/ext/couchbase/core/protocol/cmd_decrement.cxx +6 -6
  236. data/ext/couchbase/core/protocol/cmd_get.cxx +1 -1
  237. data/ext/couchbase/core/protocol/cmd_get_and_lock.cxx +2 -2
  238. data/ext/couchbase/core/protocol/cmd_get_and_touch.cxx +2 -2
  239. data/ext/couchbase/core/protocol/cmd_get_cluster_config.cxx +6 -4
  240. data/ext/couchbase/core/protocol/cmd_get_collection_id.cxx +2 -2
  241. data/ext/couchbase/core/protocol/cmd_get_error_map.cxx +1 -1
  242. data/ext/couchbase/core/protocol/cmd_get_meta.cxx +4 -4
  243. data/ext/couchbase/core/protocol/cmd_get_replica.cxx +1 -1
  244. data/ext/couchbase/core/protocol/cmd_hello.cxx +1 -1
  245. data/ext/couchbase/core/protocol/cmd_increment.cxx +6 -6
  246. data/ext/couchbase/core/protocol/cmd_insert.cxx +4 -4
  247. data/ext/couchbase/core/protocol/cmd_lookup_in.cxx +4 -3
  248. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +4 -3
  249. data/ext/couchbase/core/protocol/cmd_mutate_in.cxx +10 -9
  250. data/ext/couchbase/core/protocol/cmd_mutate_in.hxx +10 -10
  251. data/ext/couchbase/core/protocol/cmd_observe_seqno.cxx +7 -7
  252. data/ext/couchbase/core/protocol/cmd_prepend.cxx +2 -2
  253. data/ext/couchbase/core/protocol/cmd_remove.cxx +2 -2
  254. data/ext/couchbase/core/protocol/cmd_replace.cxx +4 -4
  255. data/ext/couchbase/core/protocol/cmd_touch.cxx +1 -1
  256. data/ext/couchbase/core/protocol/cmd_upsert.cxx +4 -4
  257. data/ext/couchbase/core/protocol/datatype.hxx +118 -1
  258. data/ext/couchbase/core/protocol/frame_info_utils.cxx +1 -1
  259. data/ext/couchbase/core/protocol/magic.hxx +4 -0
  260. data/ext/couchbase/core/protocol/magic_fmt.hxx +3 -0
  261. data/ext/couchbase/core/protocol/server_request.hxx +3 -1
  262. data/ext/couchbase/core/protocol/status.cxx +1 -1
  263. data/ext/couchbase/core/protostellar/analytics_converter.hxx +171 -0
  264. data/ext/couchbase/core/protostellar/bucket_admin_converter.hxx +301 -0
  265. data/ext/couchbase/core/protostellar/collection_admin_converter.hxx +92 -0
  266. data/ext/couchbase/core/protostellar/component.cxx +2445 -0
  267. data/ext/couchbase/core/protostellar/component.hxx +391 -0
  268. data/ext/couchbase/core/{columnar/bootstrap_notification_subscriber.hxx → protostellar/compression_settings.hxx} +11 -10
  269. data/ext/couchbase/core/protostellar/credentials.cxx +233 -0
  270. data/ext/couchbase/core/protostellar/credentials.hxx +88 -0
  271. data/ext/couchbase/core/protostellar/dispatcher.cxx +57 -0
  272. data/ext/couchbase/core/protostellar/dispatcher.hxx +482 -0
  273. data/ext/couchbase/core/protostellar/error_utils.cxx +293 -0
  274. data/ext/couchbase/core/protostellar/error_utils.hxx +97 -0
  275. data/ext/couchbase/core/protostellar/json_payload.hxx +49 -0
  276. data/ext/couchbase/core/protostellar/kv_converter.cxx +70 -0
  277. data/ext/couchbase/core/protostellar/kv_converter.hxx +600 -0
  278. data/ext/couchbase/core/protostellar/query_converter.hxx +265 -0
  279. data/ext/couchbase/core/protostellar/query_index_admin_converter.hxx +291 -0
  280. data/ext/couchbase/core/protostellar/query_proto.hxx +73 -0
  281. data/ext/couchbase/core/protostellar/search_converter.hxx +244 -0
  282. data/ext/couchbase/core/protostellar/search_index_admin_converter.hxx +156 -0
  283. data/ext/couchbase/core/protostellar/view_converter.hxx +158 -0
  284. data/ext/couchbase/core/query_stream.cxx +447 -0
  285. data/ext/couchbase/core/query_stream.hxx +141 -0
  286. data/ext/couchbase/core/query_stream_component.cxx +243 -0
  287. data/ext/couchbase/core/query_stream_component.hxx +68 -0
  288. data/ext/couchbase/core/range_scan_load_balancer.cxx +15 -8
  289. data/ext/couchbase/core/range_scan_orchestrator.cxx +15 -3
  290. data/ext/couchbase/core/response_handler.hxx +41 -4
  291. data/ext/couchbase/core/row_streamer.cxx +332 -46
  292. data/ext/couchbase/core/row_streamer.hxx +31 -1
  293. data/ext/couchbase/core/row_streamer_options.hxx +55 -0
  294. data/ext/couchbase/core/sasl/client.h +3 -0
  295. data/ext/couchbase/core/search_scoring.hxx +66 -0
  296. data/ext/couchbase/core/stream_error_details.hxx +69 -0
  297. data/ext/couchbase/core/timeout_defaults.hxx +0 -1
  298. data/ext/couchbase/core/topology/capabilities.hxx +6 -0
  299. data/ext/couchbase/core/topology/capabilities_fmt.hxx +3 -0
  300. data/ext/couchbase/core/topology/configuration.cxx +21 -6
  301. data/ext/couchbase/core/topology/configuration.hxx +29 -8
  302. data/ext/couchbase/core/topology/configuration_json.hxx +3 -0
  303. data/ext/couchbase/core/topology/error_map_json.hxx +3 -1
  304. data/ext/couchbase/core/tracing/threshold_logging_tracer.cxx +70 -4
  305. data/ext/couchbase/core/tracing/threshold_logging_tracer.hxx +10 -0
  306. data/ext/couchbase/core/transactions/active_transaction_record.cxx +0 -34
  307. data/ext/couchbase/core/transactions/attempt_context_impl.cxx +108 -63
  308. data/ext/couchbase/core/transactions/attempt_context_impl.hxx +19 -2
  309. data/ext/couchbase/core/transactions/error_list.hxx +2 -2
  310. data/ext/couchbase/core/transactions/get_multi_fetch.hxx +160 -0
  311. data/ext/couchbase/core/transactions/get_multi_orchestrator.cxx +326 -125
  312. data/ext/couchbase/core/transactions/get_multi_transaction_id.hxx +62 -0
  313. data/ext/couchbase/core/transactions/internal/atr_entry.hxx +22 -2
  314. data/ext/couchbase/core/transactions/internal/transaction_context.hxx +2 -2
  315. data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +23 -1
  316. data/ext/couchbase/core/transactions/internal/utils.hxx +50 -14
  317. data/ext/couchbase/core/transactions/staged_mutation.cxx +23 -18
  318. data/ext/couchbase/core/transactions/staged_mutation.hxx +15 -4
  319. data/ext/couchbase/core/transactions/transaction_context.cxx +55 -40
  320. data/ext/couchbase/core/transactions/transaction_get_result.hxx +16 -0
  321. data/ext/couchbase/core/transactions/transactions.cxx +3 -55
  322. data/ext/couchbase/core/transactions/transactions_cleanup.cxx +165 -72
  323. data/ext/couchbase/core/transactions/transactions_config.cxx +0 -11
  324. data/ext/couchbase/core/transactions/utils.cxx +10 -0
  325. data/ext/couchbase/core/transactions/waitable_op_list.hxx +2 -2
  326. data/ext/couchbase/core/transactions.hxx +9 -9
  327. data/ext/couchbase/core/utils/byteswap.hxx +166 -9
  328. data/ext/couchbase/core/utils/connection_string.cxx +50 -85
  329. data/ext/couchbase/core/utils/connection_string.hxx +15 -0
  330. data/ext/couchbase/core/{columnar/backoff_calculator.hxx → utils/deadline.hxx} +13 -13
  331. data/ext/couchbase/core/utils/duration_parser.cxx +1 -1
  332. data/ext/couchbase/core/utils/json_streaming_lexer.cxx +48 -5
  333. data/ext/couchbase/core/utils/json_streaming_lexer.hxx +4 -1
  334. data/ext/couchbase/core/utils/movable_function.hxx +301 -52
  335. data/ext/couchbase/core/utils/name_codec.hxx +154 -11
  336. data/ext/couchbase/core/{columnar/retry_info.hxx → utils/process_id.hxx} +18 -13
  337. data/ext/couchbase/core/utils/url_codec.cxx +12 -51
  338. data/ext/couchbase/core/websocket_codec.cxx +3 -29
  339. data/ext/couchbase/couchbase/analytics_options.hxx +21 -10
  340. data/ext/couchbase/couchbase/analytics_row.hxx +105 -0
  341. data/ext/couchbase/couchbase/analytics_stream_result.hxx +266 -0
  342. data/ext/couchbase/couchbase/boolean_query.hxx +3 -3
  343. data/ext/couchbase/couchbase/bucket.hxx +57 -0
  344. data/ext/couchbase/couchbase/cluster.hxx +183 -5
  345. data/ext/couchbase/{core/columnar/security_options.hxx → couchbase/cluster_state.hxx} +18 -14
  346. data/ext/couchbase/couchbase/codec/lenient_json_transcoder.hxx +69 -0
  347. data/ext/couchbase/couchbase/collection.hxx +234 -14
  348. data/ext/couchbase/couchbase/common_durability_options.hxx +18 -3
  349. data/ext/couchbase/couchbase/conjunction_query.hxx +1 -1
  350. data/ext/couchbase/couchbase/create_dataset_analytics_options.hxx +6 -1
  351. data/ext/couchbase/couchbase/date_range_query.hxx +2 -2
  352. data/ext/couchbase/couchbase/decrement_options.hxx +1 -1
  353. data/ext/couchbase/couchbase/disjunction_query.hxx +1 -1
  354. data/ext/couchbase/couchbase/error.hxx +32 -0
  355. data/ext/couchbase/couchbase/error_codes.hxx +32 -0
  356. data/ext/couchbase/couchbase/geo_bounding_box_query.hxx +1 -1
  357. data/ext/couchbase/couchbase/geo_distance_query.hxx +1 -1
  358. data/ext/couchbase/couchbase/geo_polygon_query.hxx +1 -1
  359. data/ext/couchbase/couchbase/get_replica_options.hxx +68 -0
  360. data/ext/couchbase/couchbase/get_replica_result.hxx +5 -3
  361. data/ext/couchbase/couchbase/get_replica_strategy.hxx +161 -0
  362. data/ext/couchbase/couchbase/get_result.hxx +1 -0
  363. data/ext/couchbase/couchbase/increment_options.hxx +1 -1
  364. data/ext/couchbase/couchbase/insert_options.hxx +1 -1
  365. data/ext/couchbase/couchbase/match_phrase_query.hxx +1 -1
  366. data/ext/couchbase/couchbase/match_query.hxx +1 -1
  367. data/ext/couchbase/couchbase/mutate_in_options.hxx +3 -3
  368. data/ext/couchbase/couchbase/network_options.hxx +1 -1
  369. data/ext/couchbase/couchbase/node_id.hxx +42 -14
  370. data/ext/couchbase/couchbase/node_id_for_options.hxx +7 -1
  371. data/ext/couchbase/couchbase/node_ids_options.hxx +8 -2
  372. data/ext/couchbase/couchbase/numeric_range_query.hxx +1 -1
  373. data/ext/couchbase/couchbase/phrase_query.hxx +1 -1
  374. data/ext/couchbase/couchbase/prefix_query.hxx +1 -1
  375. data/ext/couchbase/couchbase/query_options.hxx +23 -12
  376. data/ext/couchbase/couchbase/query_row.hxx +111 -0
  377. data/ext/couchbase/couchbase/query_stream_result.hxx +302 -0
  378. data/ext/couchbase/couchbase/query_string_query.hxx +3 -3
  379. data/ext/couchbase/couchbase/regexp_query.hxx +1 -1
  380. data/ext/couchbase/couchbase/replace_options.hxx +3 -3
  381. data/ext/couchbase/couchbase/result.hxx +14 -3
  382. data/ext/couchbase/couchbase/scan_result_item.hxx +1 -0
  383. data/ext/couchbase/couchbase/scope.hxx +85 -2
  384. data/ext/couchbase/couchbase/search_options.hxx +58 -8
  385. data/ext/couchbase/couchbase/search_scoring.hxx +94 -0
  386. data/ext/couchbase/couchbase/search_scoring_none.hxx +51 -0
  387. data/ext/couchbase/couchbase/search_scoring_reciprocal_rank_fusion.hxx +88 -0
  388. data/ext/couchbase/couchbase/search_scoring_relative_score_fusion.hxx +70 -0
  389. data/ext/couchbase/couchbase/term_range_query.hxx +1 -1
  390. data/ext/couchbase/couchbase/tracing/request_span.hxx +46 -0
  391. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +23 -21
  392. data/ext/couchbase/couchbase/transactions/transaction_get_multi_result.hxx +24 -19
  393. data/ext/couchbase/couchbase/transactions/transaction_get_result.hxx +17 -3
  394. data/ext/couchbase/couchbase/transactions/transactions_config.hxx +4 -2
  395. data/ext/couchbase/couchbase/transactions.hxx +27 -18
  396. data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
  397. data/ext/couchbase/couchbase/vector_query.hxx +1 -1
  398. data/ext/couchbase/couchbase/wait_until_ready_options.hxx +111 -0
  399. data/ext/couchbase/couchbase/wildcard_query.hxx +1 -1
  400. data/ext/extconf.rb +15 -10
  401. data/ext/rcb_analytics.cxx +82 -102
  402. data/ext/rcb_backend.cxx +42 -36
  403. data/ext/rcb_backend.hxx +2 -0
  404. data/ext/rcb_buckets.cxx +47 -59
  405. data/ext/rcb_collections.cxx +42 -52
  406. data/ext/rcb_crud.cxx +345 -224
  407. data/ext/rcb_diagnostics.cxx +117 -27
  408. data/ext/rcb_exceptions.cxx +152 -99
  409. data/ext/rcb_exceptions.hxx +25 -0
  410. data/ext/rcb_extras.cxx +18 -19
  411. data/ext/rcb_hdr_histogram.cxx +19 -4
  412. data/ext/rcb_logger.cxx +202 -27
  413. data/ext/rcb_logger.hxx +12 -0
  414. data/ext/rcb_multi.cxx +40 -40
  415. data/ext/rcb_observability.cxx +7 -5
  416. data/ext/rcb_query.cxx +99 -142
  417. data/ext/rcb_range_scan.cxx +41 -35
  418. data/ext/rcb_search.cxx +98 -99
  419. data/ext/rcb_users.cxx +36 -56
  420. data/ext/rcb_utils.cxx +100 -37
  421. data/ext/rcb_utils.hxx +287 -33
  422. data/ext/rcb_version.cxx +1 -1
  423. data/ext/rcb_views.cxx +55 -51
  424. data/lib/active_support/cache/couchbase_store.rb +77 -12
  425. data/lib/couchbase/bucket.rb +20 -0
  426. data/lib/couchbase/cluster.rb +17 -0
  427. data/lib/couchbase/collection.rb +36 -1
  428. data/lib/couchbase/errors.rb +18 -0
  429. data/lib/couchbase/fork_hooks.rb +16 -7
  430. data/lib/couchbase/get_replica_strategy.rb +89 -0
  431. data/lib/couchbase/logger.rb +1 -1
  432. data/lib/couchbase/management/analytics_index_manager.rb +15 -15
  433. data/lib/couchbase/management/bucket_manager.rb +6 -6
  434. data/lib/couchbase/management/collection_manager.rb +6 -6
  435. data/lib/couchbase/management/query_index_manager.rb +7 -7
  436. data/lib/couchbase/management/search_index_manager.rb +13 -13
  437. data/lib/couchbase/management/user_manager.rb +10 -10
  438. data/lib/couchbase/management/view_index_manager.rb +5 -5
  439. data/lib/couchbase/options.rb +174 -42
  440. data/lib/couchbase/protostellar/bucket.rb +4 -0
  441. data/lib/couchbase/protostellar/cluster.rb +4 -0
  442. data/lib/couchbase/protostellar/collection.rb +4 -0
  443. data/lib/couchbase/protostellar/error_handling.rb +2 -1
  444. data/lib/couchbase/protostellar/request.rb +1 -0
  445. data/lib/couchbase/protostellar/request_generator/kv.rb +16 -2
  446. data/lib/couchbase/protostellar/request_generator/search.rb +17 -1
  447. data/lib/couchbase/protostellar/retry/orchestrator.rb +3 -2
  448. data/lib/couchbase/protostellar/retry/strategies/fail_fast.rb +33 -0
  449. data/lib/couchbase/protostellar/retry/strategies.rb +1 -0
  450. data/lib/couchbase/search_options.rb +95 -6
  451. data/lib/couchbase/version.rb +1 -1
  452. metadata +91 -33
  453. data/ext/0001-fix-build-for-mingw-w64-ucrt-x86_64-toolchain.patch +0 -40
  454. data/ext/couchbase/core/columnar/agent.hxx +0 -71
  455. data/ext/couchbase/core/columnar/agent_config.hxx +0 -59
  456. data/ext/couchbase/core/columnar/background_bootstrap_listener.hxx +0 -42
  457. data/ext/couchbase/core/columnar/backoff_calculator.cxx +0 -62
  458. data/ext/couchbase/core/columnar/columnar_agent.cxx +0 -147
  459. data/ext/couchbase/core/columnar/columnar_agent_config.cxx +0 -48
  460. data/ext/couchbase/core/columnar/columnar_error.cxx +0 -51
  461. data/ext/couchbase/core/columnar/database_management_options.hxx +0 -56
  462. data/ext/couchbase/core/columnar/error.hxx +0 -51
  463. data/ext/couchbase/core/columnar/error_codes.cxx +0 -107
  464. data/ext/couchbase/core/columnar/error_codes.hxx +0 -75
  465. data/ext/couchbase/core/columnar/management_component.cxx +0 -300
  466. data/ext/couchbase/core/columnar/management_component.hxx +0 -56
  467. data/ext/couchbase/core/columnar/query_component.cxx +0 -523
  468. data/ext/couchbase/core/columnar/query_component.hxx +0 -57
  469. data/ext/couchbase/core/columnar/query_options.hxx +0 -59
  470. data/ext/couchbase/core/columnar/query_result.cxx +0 -130
  471. data/ext/couchbase/core/columnar/query_result.hxx +0 -78
  472. data/ext/couchbase/core/io/config_tracker.cxx +0 -687
  473. data/ext/couchbase/core/io/config_tracker.hxx +0 -123
@@ -19,15 +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
25
22
  #include "configuration_belongs_to_session.hxx"
23
+ #include "opaque_ring_table.hxx"
24
+
25
+ #include "core/app_telemetry_meter.hxx"
26
26
  #include "core/config_listener.hxx"
27
27
  #include "core/diagnostics.hxx"
28
28
  #include "core/impl/bootstrap_error.hxx"
29
29
  #include "core/impl/bootstrap_state_listener.hxx"
30
30
  #include "core/logger/logger.hxx"
31
+ #include "core/logger/redaction.hxx"
31
32
  #include "core/mcbp/codec.hxx"
32
33
  #include "core/mcbp/queue_request.hxx"
33
34
  #include "core/meta/version.hxx"
@@ -51,8 +52,10 @@
51
52
  #include "core/sasl/error_fmt.h"
52
53
  #include "core/topology/capabilities_fmt.hxx"
53
54
  #include "core/topology/configuration_fmt.hxx"
55
+ #include "mcbp_buffer_pool.hxx"
54
56
  #include "mcbp_context.hxx"
55
57
  #include "mcbp_message.hxx"
58
+ #include "mcbp_output_queue.hxx"
56
59
  #include "mcbp_parser.hxx"
57
60
  #include "retry_orchestrator.hxx"
58
61
  #include "streams.hxx"
@@ -110,7 +113,7 @@ struct mcbp_header_layout {
110
113
  if (magic == 0x18 || magic == 0x08) {
111
114
  return keylen.alt.key;
112
115
  }
113
- return couchbase::core::utils::byte_swap(keylen.normal);
116
+ return couchbase::core::utils::network_to_host(keylen.normal);
114
117
  }
115
118
 
116
119
  [[nodiscard]] constexpr auto framing_extras_length() const -> std::uint8_t
@@ -150,10 +153,10 @@ struct fmt::formatter<mcbp_header_view<Container>> {
150
153
  header->extlen,
151
154
  header->datatype,
152
155
  header->specific_name(),
153
- couchbase::core::utils::byte_swap(header->specific),
154
- couchbase::core::utils::byte_swap(header->bodylen),
155
- couchbase::core::utils::byte_swap(header->opaque),
156
- 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));
157
160
  }
158
161
  };
159
162
 
@@ -270,13 +273,21 @@ class mcbp_session_impl
270
273
  hello_req.body().enable_mutation_tokens();
271
274
  }
272
275
  hello_req.opaque(session_->next_opaque());
273
- auto user_agent = meta::user_agent_for_mcbp(
274
- 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);
275
279
  hello_req.body().user_agent(user_agent);
276
- CB_LOG_DEBUG("{} user_agent={}, requested_features=[{}]",
277
- session_->log_prefix_,
278
- user_agent,
279
- 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(), ", ")));
280
291
  session_->write(hello_req.data());
281
292
 
282
293
  if (!session_->origin_.credentials().uses_certificate()) {
@@ -371,7 +382,10 @@ class mcbp_session_impl
371
382
  case protocol::client_opcode::hello: {
372
383
  protocol::client_response<protocol::hello_response_body> resp(std::move(msg));
373
384
  if (resp.status() == key_value_status_code::success) {
374
- 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
+ }
375
389
  CB_LOG_DEBUG("{} supported_features=[{}]",
376
390
  session_->log_prefix_,
377
391
  utils::join_strings_fmt(session_->supported_features_, ", "));
@@ -496,7 +510,7 @@ class mcbp_session_impl
496
510
  if (resp.status() == key_value_status_code::success) {
497
511
  CB_LOG_DEBUG("{} selected bucket: {}",
498
512
  session_->log_prefix_,
499
- session_->bucket_name_.value_or(""));
513
+ logger::metadata(session_->bucket_name_.value_or("")));
500
514
  session_->bucket_selected_ = true;
501
515
  } else if (resp.status() == key_value_status_code::not_found) {
502
516
  auto error_msg =
@@ -544,12 +558,14 @@ class mcbp_session_impl
544
558
  if (session_->origin_.options().dump_configuration) {
545
559
  if (const auto& text = resp.body().config_text(); text.has_value()) {
546
560
  CB_LOG_TRACE("{} configuration from get_cluster_config request (bootstrap, "
547
- "size={}, endpoint=\"{}:{}\"), {}",
561
+ "size={}, endpoint=\"{}\"), {}",
548
562
  session_->log_prefix_,
549
563
  text.value().size(),
550
- info.endpoint_address,
551
- info.endpoint_port,
552
- 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()));
553
569
  }
554
570
  }
555
571
  if (resp.status() == key_value_status_code::success) {
@@ -626,12 +642,14 @@ class mcbp_session_impl
626
642
  if (session_->origin_.options().dump_configuration) {
627
643
  if (const auto& text = req.body().config_text(); text.has_value()) {
628
644
  CB_LOG_TRACE("{} configuration from cluster_map_change_notification request "
629
- "(size={}, endpoint=\"{}:{}\"), {}",
645
+ "(size={}, endpoint=\"{}\"), {}",
630
646
  session_->log_prefix_,
631
647
  text.value().size(),
632
- info.endpoint_address,
633
- info.endpoint_port,
634
- 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()));
635
653
  }
636
654
  }
637
655
  std::optional<topology::configuration> config = req.body().config();
@@ -641,24 +659,25 @@ class mcbp_session_impl
641
659
  }
642
660
  } break;
643
661
  default:
644
- CB_LOG_WARNING("{} unexpected server request: opcode={:x}, opaque={}{:a}{:a}",
662
+ CB_LOG_WARNING("{} unexpected server request: opcode={:x}, opaque={}{:a}",
645
663
  session_->log_prefix_,
646
664
  msg.header.opcode,
647
- utils::byte_swap(msg.header.opaque),
648
- spdlog::to_hex(msg.header_data()),
649
- 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()));
650
668
  }
651
669
  break;
652
670
  case protocol::magic::client_request:
653
671
  case protocol::magic::alt_client_request:
654
672
  case protocol::magic::server_response:
655
- 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}",
656
675
  session_->log_prefix_,
657
676
  magic,
658
677
  msg.header.opcode,
659
- utils::byte_swap(msg.header.opaque),
660
- spdlog::to_hex(msg.header_data()),
661
- 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()));
662
681
  break;
663
682
  }
664
683
  }
@@ -713,12 +732,14 @@ class mcbp_session_impl
713
732
  if (session_->origin_.options().dump_configuration) {
714
733
  if (const auto& text = resp.body().config_text(); text.has_value()) {
715
734
  CB_LOG_TRACE("{} configuration from get_cluster_config response (size={}, "
716
- "endpoint=\"{}:{}\"), {}",
735
+ "endpoint=\"{}\"), {}",
717
736
  session_->log_prefix_,
718
737
  text.value().size(),
719
- info.endpoint_address,
720
- info.endpoint_port,
721
- 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()));
722
743
  }
723
744
  }
724
745
  if (resp.status() == key_value_status_code::success) {
@@ -763,7 +784,7 @@ class mcbp_session_impl
763
784
  case protocol::client_opcode::subdoc_multi_lookup:
764
785
  case protocol::client_opcode::subdoc_multi_mutation:
765
786
  case protocol::client_opcode::sasl_auth: {
766
- const std::uint16_t status = utils::byte_swap(msg.header.specific);
787
+ const std::uint16_t status = utils::network_to_host(msg.header.specific);
767
788
  if (status == static_cast<std::uint16_t>(key_value_status_code::not_my_vbucket)) {
768
789
  session_->handle_not_my_vbucket(msg);
769
790
  }
@@ -771,12 +792,12 @@ class mcbp_session_impl
771
792
  CB_LOG_WARNING("{} received auth stale status for {}, opaque={}",
772
793
  session_->log_prefix_,
773
794
  protocol::client_opcode(msg.header.opcode),
774
- utils::byte_swap(msg.header.opaque));
795
+ utils::network_to_host(msg.header.opaque));
775
796
  session_->stop(retry_reason::do_not_retry);
776
797
  return;
777
798
  }
778
799
 
779
- std::uint32_t opaque = utils::byte_swap(msg.header.opaque);
800
+ std::uint32_t opaque = utils::network_to_host(msg.header.opaque);
780
801
  if (session_->handle_request(opcode, status, opaque, std::move(msg))) {
781
802
  CB_LOG_TRACE("{} MCBP invoked operation handler: opcode={}, opaque={}, status={}",
782
803
  session_->log_prefix_,
@@ -792,12 +813,12 @@ class mcbp_session_impl
792
813
  }
793
814
  } break;
794
815
  default:
795
- CB_LOG_WARNING("{} unexpected client response: opcode={}, opaque={}{:a}{:a})",
816
+ CB_LOG_WARNING("{} unexpected client response: opcode={}, opaque={}{:a})",
796
817
  session_->log_prefix_,
797
818
  opcode,
798
819
  msg.header.opaque,
799
- spdlog::to_hex(msg.header_data()),
800
- 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()));
801
822
  }
802
823
  break;
803
824
  case protocol::magic::server_request:
@@ -811,12 +832,14 @@ class mcbp_session_impl
811
832
  if (session_->origin_.options().dump_configuration) {
812
833
  if (const auto& text = req.body().config_text(); text.has_value()) {
813
834
  CB_LOG_TRACE("{} configuration from cluster_map_change_notification request "
814
- "(size={}, endpoint=\"{}:{}\"), {}",
835
+ "(size={}, endpoint=\"{}\"), {}",
815
836
  session_->log_prefix_,
816
837
  text.value().size(),
817
- info.endpoint_address,
818
- info.endpoint_port,
819
- 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()));
820
843
  }
821
844
  }
822
845
  std::optional<topology::configuration> config = req.body().config();
@@ -826,24 +849,25 @@ class mcbp_session_impl
826
849
  }
827
850
  } break;
828
851
  default:
829
- CB_LOG_WARNING("{} unexpected server request: opcode={:x}, opaque={}{:a}{:a}",
852
+ CB_LOG_WARNING("{} unexpected server request: opcode={:x}, opaque={}{:a}",
830
853
  session_->log_prefix_,
831
854
  msg.header.opcode,
832
855
  msg.header.opaque,
833
- spdlog::to_hex(msg.header_data()),
834
- 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()));
835
858
  }
836
859
  break;
837
860
  case protocol::magic::client_request:
838
861
  case protocol::magic::alt_client_request:
839
862
  case protocol::magic::server_response:
840
- 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}",
841
865
  session_->log_prefix_,
842
866
  magic,
843
867
  msg.header.opcode,
844
868
  msg.header.opaque,
845
- spdlog::to_hex(msg.header_data()),
846
- 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()));
847
871
  break;
848
872
  }
849
873
  }
@@ -877,7 +901,7 @@ public:
877
901
  , codec_{ { supported_features_.begin(), supported_features_.end() } }
878
902
  {
879
903
  log_prefix_ = fmt::format(
880
- "[{}/{}/{}/{}]", client_id_, id_, stream_->log_prefix(), bucket_name_.value_or("-"));
904
+ "[{}/{}/{}/{}]", client_id_, id_, stream_->log_prefix(), log_prefix_bucket_name());
881
905
  }
882
906
 
883
907
  mcbp_session_impl(std::string_view client_id,
@@ -907,7 +931,7 @@ public:
907
931
  , codec_{ { supported_features_.begin(), supported_features_.end() } }
908
932
  {
909
933
  log_prefix_ = fmt::format(
910
- "[{}/{}/{}/{}]", client_id_, id_, stream_->log_prefix(), bucket_name_.value_or("-"));
934
+ "[{}/{}/{}/{}]", client_id_, id_, stream_->log_prefix(), log_prefix_bucket_name());
911
935
  }
912
936
 
913
937
  mcbp_session_impl(const mcbp_session_impl&) = delete;
@@ -921,8 +945,9 @@ public:
921
945
  stop(retry_reason::do_not_retry);
922
946
  }
923
947
 
924
- [[nodiscard]] auto log_prefix() const -> const std::string&
948
+ [[nodiscard]] auto log_prefix() const -> std::string
925
949
  {
950
+ const std::scoped_lock lock(session_info_mutex_);
926
951
  return log_prefix_;
927
952
  }
928
953
 
@@ -1054,6 +1079,7 @@ public:
1054
1079
 
1055
1080
  [[nodiscard]] auto context() const -> mcbp_context
1056
1081
  {
1082
+ const std::scoped_lock lock(session_info_mutex_);
1057
1083
  return { config_, supported_features_ };
1058
1084
  }
1059
1085
 
@@ -1150,7 +1176,8 @@ public:
1150
1176
  }
1151
1177
 
1152
1178
  if (reason == retry_reason::do_not_retry || reason == retry_reason::unknown) {
1153
- CB_LOG_WARNING("{} reauthentication failed (code={}, message={}, reason={}, opaque={})",
1179
+ CB_LOG_WARNING("{} reauthentication failed (code={}, message={}, reason={}, opaque={}), "
1180
+ "closing connection",
1154
1181
  self->log_prefix_,
1155
1182
  ec.value(),
1156
1183
  ec.message(),
@@ -1158,6 +1185,7 @@ public:
1158
1185
  req.opaque());
1159
1186
  self->reauth_deadline_.cancel();
1160
1187
  self->reauth_in_progress_ = false;
1188
+ self->stop(retry_reason::do_not_retry);
1161
1189
  return;
1162
1190
  }
1163
1191
 
@@ -1199,45 +1227,6 @@ public:
1199
1227
  self->state_listener_->report_bootstrap_error(
1200
1228
  fmt::format("{}:{}", self->bootstrap_hostname_, self->bootstrap_port_), ec);
1201
1229
  }
1202
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
1203
- // an existing background bootstrap listener means we want to infinitely
1204
- // attempt to connect.
1205
- if (self->background_bootstrap_listener_) {
1206
- if (self->last_bootstrap_error_.has_value()) {
1207
- self->background_bootstrap_listener_->notify_bootstrap_error(
1208
- self->last_bootstrap_error_.value());
1209
- } else {
1210
- self->background_bootstrap_listener_->notify_bootstrap_error(
1211
- { make_error_code(errc::common::unambiguous_timeout),
1212
- "Unable to connect in time.",
1213
- self->bootstrap_hostname_,
1214
- self->bootstrap_port_ });
1215
- }
1216
- auto backoff = std::chrono::milliseconds(500);
1217
- CB_LOG_DEBUG("{} unable to connect in time, waiting for {}ms before retry",
1218
- self->log_prefix_,
1219
- backoff.count());
1220
- self->retry_backoff_.expires_after(backoff);
1221
- self->retry_backoff_.async_wait([self](std::error_code ec) mutable {
1222
- if (ec == asio::error::operation_aborted || self->stopped_) {
1223
- return;
1224
- }
1225
- self->origin_.restart();
1226
- self->initiate_bootstrap();
1227
- });
1228
- } else {
1229
- if (!ec) {
1230
- ec = errc::common::unambiguous_timeout;
1231
- }
1232
- CB_LOG_WARNING("{} unable to bootstrap in time, bootstrap_timeout: {}",
1233
- self->log_prefix_,
1234
- bootstrap_timeout);
1235
- if (auto h = std::move(self->bootstrap_callback_); h) {
1236
- h(ec, {});
1237
- }
1238
- self->stop(retry_reason::do_not_retry);
1239
- }
1240
- #else
1241
1230
  if (!ec) {
1242
1231
  ec = errc::common::unambiguous_timeout;
1243
1232
  }
@@ -1248,7 +1237,6 @@ public:
1248
1237
  h(ec, {});
1249
1238
  }
1250
1239
  self->stop(retry_reason::do_not_retry);
1251
- #endif
1252
1240
  });
1253
1241
  initiate_bootstrap();
1254
1242
  }
@@ -1269,25 +1257,12 @@ public:
1269
1257
  self->log_prefix_,
1270
1258
  old_id,
1271
1259
  self->stream_->id(),
1272
- self->bootstrap_hostname_,
1273
- self->bootstrap_port_);
1260
+ logger::system_data(self->bootstrap_hostname_),
1261
+ logger::system_data(self->bootstrap_port_));
1274
1262
  return self->initiate_bootstrap();
1275
1263
  });
1276
1264
  }
1277
1265
  if (origin_.exhausted()) {
1278
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
1279
- if (background_bootstrap_listener_) {
1280
- if (last_bootstrap_error_.has_value()) {
1281
- background_bootstrap_listener_->notify_bootstrap_error(last_bootstrap_error_.value());
1282
- } else {
1283
- background_bootstrap_listener_->notify_bootstrap_error(
1284
- { errc::network::no_endpoints_left,
1285
- "Reached end of list of bootstrap nodes.",
1286
- bootstrap_hostname_,
1287
- bootstrap_port_ });
1288
- }
1289
- }
1290
- #endif
1291
1266
  auto backoff = std::chrono::milliseconds(500);
1292
1267
  CB_LOG_DEBUG("{} reached the end of list of bootstrap nodes, waiting for {}ms before restart",
1293
1268
  log_prefix_,
@@ -1306,12 +1281,15 @@ public:
1306
1281
  bootstrap_port_number_ =
1307
1282
  gsl::narrow_cast<std::uint16_t>(std::stoul(bootstrap_port_, nullptr, 10));
1308
1283
  bootstrap_address_ = fmt::format("{}:{}", bootstrap_hostname_, bootstrap_port_);
1309
- log_prefix_ = fmt::format("[{}/{}/{}/{}] <{}>",
1310
- client_id_,
1311
- id_,
1312
- stream_->log_prefix(),
1313
- bucket_name_.value_or("-"),
1314
- 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
+ }
1315
1293
  CB_LOG_DEBUG("{} attempt to establish MCBP connection", log_prefix_);
1316
1294
 
1317
1295
  resolve_deadline_.expires_after(origin_.options().resolve_timeout);
@@ -1340,6 +1318,12 @@ public:
1340
1318
  return node_uuid_;
1341
1319
  }
1342
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
+
1343
1327
  [[nodiscard]] auto is_stopped() const -> bool
1344
1328
  {
1345
1329
  return stopped_;
@@ -1362,33 +1346,48 @@ public:
1362
1346
  }
1363
1347
 
1364
1348
  if (reason == retry_reason::socket_closed_while_in_flight && !bootstrapped_) {
1365
- return stream_->close([self = shared_from_this(), old_id = stream_->id()](std::error_code) {
1366
- CB_LOG_DEBUG(
1367
- R"({} reopened socket connection due to IO error, "{}" -> "{}", host="{}", port={})",
1368
- self->log_prefix_,
1369
- old_id,
1370
- self->stream_->id(),
1371
- self->bootstrap_hostname_,
1372
- self->bootstrap_port_);
1373
- return self->initiate_bootstrap();
1374
- });
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
+ });
1375
1365
  }
1376
1366
 
1377
1367
  state_ = diag::endpoint_state::disconnecting;
1378
- 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);
1379
1374
  stopped_ = true;
1380
- bootstrap_deadline_.cancel();
1381
- resolve_deadline_.cancel();
1382
- connection_deadline_.cancel();
1383
- reauth_deadline_.cancel();
1384
- retry_backoff_.cancel();
1385
- ping_timeout_.cancel();
1386
- resolver_.cancel();
1387
- 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
+ });
1388
1390
  });
1389
- if (auto h = std::move(bootstrap_handler_); h) {
1390
- h->stop();
1391
- }
1392
1391
  if (auto h = std::move(handler_); h) {
1393
1392
  h->stop();
1394
1393
  }
@@ -1399,18 +1398,23 @@ public:
1399
1398
  }
1400
1399
  }
1401
1400
  {
1402
- const std::scoped_lock lock(command_handlers_mutex_);
1403
- 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) {
1404
1409
  if (handler) {
1405
1410
  CB_LOG_DEBUG("{} MCBP cancel operation during session close, opaque={}, ec={}",
1406
- log_prefix_,
1411
+ stop_log_prefix,
1407
1412
  opaque,
1408
1413
  ec.message());
1409
1414
  auto fun = std::move(handler);
1410
1415
  fun(ec, reason, {}, {});
1411
1416
  }
1412
1417
  }
1413
- command_handlers_.clear();
1414
1418
  }
1415
1419
  {
1416
1420
  const std::scoped_lock lock(operations_mutex_);
@@ -1419,24 +1423,23 @@ public:
1419
1423
  auto& [request, handler] = operation;
1420
1424
  if (handler) {
1421
1425
  CB_LOG_DEBUG("{} MCBP cancel operation during session close, opaque={}, ec={}",
1422
- log_prefix_,
1426
+ stop_log_prefix,
1423
1427
  opaque,
1424
1428
  ec.message());
1425
- handler->handle_response(std::move(request), {}, reason, {}, {});
1429
+ handler->handle_response(
1430
+ std::move(request), io::mcbp_session(shared_from_this()), ec, reason, {}, {});
1426
1431
  }
1427
1432
  }
1428
1433
  operations_.clear();
1429
1434
  }
1430
- config_listeners_.clear();
1435
+ {
1436
+ const std::scoped_lock lock(session_info_mutex_);
1437
+ config_listeners_.clear();
1438
+ }
1431
1439
  state_ = diag::endpoint_state::disconnected;
1432
1440
  if (auto on_stop = std::move(on_stop_handler_); on_stop) {
1433
1441
  on_stop();
1434
1442
  }
1435
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
1436
- if (background_bootstrap_listener_) {
1437
- background_bootstrap_listener_ = nullptr;
1438
- }
1439
- #endif
1440
1443
  }
1441
1444
 
1442
1445
  void write(std::vector<std::byte>&& buf)
@@ -1445,8 +1448,7 @@ public:
1445
1448
  return;
1446
1449
  }
1447
1450
  CB_LOG_TRACE("{} MCBP send {}", log_prefix_, mcbp_header_view(buf));
1448
- const std::scoped_lock lock(output_buffer_mutex_);
1449
- output_buffer_.emplace_back(std::move(buf));
1451
+ output_queue_.stage(std::move(buf));
1450
1452
  }
1451
1453
 
1452
1454
  void flush()
@@ -1454,9 +1456,13 @@ public:
1454
1456
  if (stopped_) {
1455
1457
  return;
1456
1458
  }
1457
- asio::post(asio::bind_executor(ctx_, [self = shared_from_this()]() {
1458
- self->do_write();
1459
- }));
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
+ }
1460
1466
  }
1461
1467
 
1462
1468
  void write_and_flush(std::vector<std::byte>&& buf)
@@ -1464,8 +1470,13 @@ public:
1464
1470
  if (stopped_) {
1465
1471
  return;
1466
1472
  }
1467
- write(std::move(buf));
1468
- 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
+ }
1469
1480
  }
1470
1481
 
1471
1482
  void remove_request(std::shared_ptr<mcbp::queue_request> request) override
@@ -1494,11 +1505,7 @@ public:
1494
1505
  command_handler fun{};
1495
1506
  {
1496
1507
  const std::scoped_lock lock(command_handlers_mutex_);
1497
- if (auto handler = command_handlers_.find(opaque);
1498
- handler != command_handlers_.end() && handler->second) {
1499
- fun = std::move(handler->second);
1500
- command_handlers_.erase(handler);
1501
- }
1508
+ fun = command_handlers_.take(opaque);
1502
1509
  }
1503
1510
 
1504
1511
  auto reason = status == static_cast<std::uint16_t>(key_value_status_code::not_my_vbucket)
@@ -1527,6 +1534,7 @@ public:
1527
1534
  }
1528
1535
  if (request) {
1529
1536
  handler->handle_response(std::move(request),
1537
+ io::mcbp_session(shared_from_this()),
1530
1538
  protocol::map_status_code(opcode, status),
1531
1539
  reason,
1532
1540
  std::move(msg),
@@ -1551,6 +1559,7 @@ public:
1551
1559
  CB_LOG_WARNING("cancel operation while trying to write to closed mcbp session, opaque={}",
1552
1560
  opaque);
1553
1561
  handler->handle_response(request,
1562
+ io::mcbp_session(shared_from_this()),
1554
1563
  errc::common::request_canceled,
1555
1564
  retry_reason::socket_closed_while_in_flight,
1556
1565
  {},
@@ -1586,7 +1595,7 @@ public:
1586
1595
  }
1587
1596
  {
1588
1597
  const std::scoped_lock lock(command_handlers_mutex_);
1589
- command_handlers_.try_emplace(opaque, std::move(handler));
1598
+ command_handlers_.insert(opaque, std::move(handler));
1590
1599
  }
1591
1600
  if (bootstrapped_ && stream_->is_open()) {
1592
1601
  write_and_flush(std::move(data));
@@ -1608,33 +1617,34 @@ public:
1608
1617
  if (stopped_) {
1609
1618
  return false;
1610
1619
  }
1611
- command_handlers_mutex_.lock();
1612
- if (auto handler = command_handlers_.find(opaque); handler != command_handlers_.end()) {
1613
- CB_LOG_DEBUG("{} MCBP cancel operation, opaque={}, ec={} ({})",
1614
- log_prefix_,
1615
- opaque,
1616
- ec.value(),
1617
- ec.message());
1618
- if (handler->second) {
1619
- auto fun = std::move(handler->second);
1620
- command_handlers_.erase(handler);
1621
- command_handlers_mutex_.unlock();
1622
- fun(ec, reason, {}, {});
1623
- return true;
1624
- }
1620
+ std::unique_lock lock(command_handlers_mutex_);
1621
+ auto fun = command_handlers_.take(opaque);
1622
+ if (!fun) {
1623
+ return false;
1625
1624
  }
1626
- command_handlers_mutex_.unlock();
1627
- 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;
1628
1636
  }
1629
1637
 
1630
1638
  [[nodiscard]] auto supports_feature(protocol::hello_feature feature) -> bool
1631
1639
  {
1640
+ const std::scoped_lock lock(session_info_mutex_);
1632
1641
  return std::find(supported_features_.begin(), supported_features_.end(), feature) !=
1633
1642
  supported_features_.end();
1634
1643
  }
1635
1644
 
1636
1645
  [[nodiscard]] auto supported_features() const -> std::vector<protocol::hello_feature>
1637
1646
  {
1647
+ const std::scoped_lock lock(session_info_mutex_);
1638
1648
  return supported_features_;
1639
1649
  }
1640
1650
 
@@ -1704,16 +1714,6 @@ public:
1704
1714
  return ++opaque_;
1705
1715
  }
1706
1716
 
1707
- [[nodiscard]] auto last_bootstrap_error() && -> std::optional<impl::bootstrap_error>
1708
- {
1709
- return std::move(last_bootstrap_error_);
1710
- }
1711
-
1712
- [[nodiscard]] auto last_bootstrap_error() const& -> const std::optional<impl::bootstrap_error>&
1713
- {
1714
- return last_bootstrap_error_;
1715
- }
1716
-
1717
1717
  auto decode_error_code(std::uint16_t code) -> std::optional<key_value_error_map_info>
1718
1718
  {
1719
1719
  if (error_map_) {
@@ -1727,6 +1727,7 @@ public:
1727
1727
 
1728
1728
  void on_configuration_update(std::shared_ptr<config_listener> handler)
1729
1729
  {
1730
+ const std::scoped_lock lock(session_info_mutex_);
1730
1731
  config_listeners_.emplace_back(std::move(handler));
1731
1732
  }
1732
1733
 
@@ -1790,10 +1791,13 @@ public:
1790
1791
  config_.reset();
1791
1792
  config_.emplace(std::move(config));
1792
1793
  configured_ = true;
1793
- for (const auto& listener : config_listeners_) {
1794
- asio::post(asio::bind_executor(ctx_, [listener, c = config_.value()]() mutable {
1795
- return listener->update_config(std::move(c));
1796
- }));
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
+ }
1797
1801
  }
1798
1802
  }
1799
1803
 
@@ -1808,25 +1812,27 @@ public:
1808
1812
  auto magic = static_cast<protocol::magic>(msg.header.magic);
1809
1813
  const std::uint8_t extras_size = msg.header.extlen;
1810
1814
  std::uint8_t framing_extras_size = 0;
1811
- std::uint16_t key_size = utils::byte_swap(msg.header.keylen);
1815
+ std::uint16_t key_size = utils::network_to_host(msg.header.keylen);
1812
1816
  if (magic == protocol::magic::alt_client_response) {
1813
- framing_extras_size = static_cast<std::uint8_t>(msg.header.keylen >> 8U);
1814
- 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;
1815
1820
  }
1816
1821
 
1817
1822
  const std::vector<std::uint8_t>::difference_type offset =
1818
1823
  framing_extras_size + key_size + extras_size;
1819
- if (utils::byte_swap(msg.header.bodylen) - offset > 0) {
1820
- std::string_view config_text{ reinterpret_cast<const char*>(msg.body.data()) + offset,
1821
- 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) };
1822
1827
  if (origin_.options().dump_configuration) {
1823
1828
  CB_LOG_TRACE(
1824
- "{} configuration from not_my_vbucket response (size={}, endpoint=\"{}:{}\"), {}",
1829
+ "{} configuration from not_my_vbucket response (size={}, endpoint=\"{}\"), {}",
1825
1830
  log_prefix_,
1826
1831
  config_text.size(),
1827
- bootstrap_hostname_,
1828
- bootstrap_port_number_,
1829
- 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));
1830
1836
  }
1831
1837
  auto config =
1832
1838
  protocol::parse_config(config_text, bootstrap_hostname_, bootstrap_port_number_);
@@ -1834,7 +1840,7 @@ public:
1834
1840
  "{} received not_my_vbucket status for {}, opaque={} with config rev={} in the payload",
1835
1841
  log_prefix_,
1836
1842
  protocol::client_opcode(msg.header.opcode),
1837
- utils::byte_swap(msg.header.opaque),
1843
+ utils::network_to_host(msg.header.opaque),
1838
1844
  config.rev_str());
1839
1845
  update_configuration(std::move(config));
1840
1846
  }
@@ -1854,15 +1860,19 @@ public:
1854
1860
  collection_cache_.update(path, uid);
1855
1861
  }
1856
1862
 
1857
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
1858
- void add_background_bootstrap_listener(
1859
- 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
1860
1869
  {
1861
- background_bootstrap_listener_ = std::move(listener);
1870
+ if (!bucket_name_.has_value()) {
1871
+ return "-";
1872
+ }
1873
+ return fmt::format("{}", logger::metadata(bucket_name_.value()));
1862
1874
  }
1863
- #endif
1864
1875
 
1865
- private:
1866
1876
  void invoke_bootstrap_handler(std::error_code ec)
1867
1877
  {
1868
1878
  connection_deadline_.cancel();
@@ -1952,10 +1962,9 @@ private:
1952
1962
  return initiate_bootstrap();
1953
1963
  }
1954
1964
  endpoints_ = endpoints;
1955
- CB_LOG_TRACE("{} resolved \"{}:{}\" to {} endpoint(s)",
1965
+ CB_LOG_TRACE("{} resolved \"{}\" to {} endpoint(s)",
1956
1966
  log_prefix_,
1957
- bootstrap_hostname_,
1958
- bootstrap_port_,
1967
+ logger::system_data(bootstrap_address_),
1959
1968
  endpoints_.size());
1960
1969
  do_connect(endpoints_.begin());
1961
1970
  }
@@ -1969,12 +1978,10 @@ private:
1969
1978
  if (it != endpoints_.end()) {
1970
1979
  auto hostname = it->endpoint().address().to_string();
1971
1980
  auto port = it->endpoint().port();
1972
- CB_LOG_DEBUG("{} connecting to {}:{} (\"{}:{}\"), timeout={}ms",
1981
+ CB_LOG_DEBUG("{} connecting to {} (\"{}\"), timeout={}ms",
1973
1982
  log_prefix_,
1974
- hostname,
1975
- port,
1976
- bootstrap_hostname_,
1977
- bootstrap_port_,
1983
+ logger::system_data(fmt::format("{}:{}", hostname, port)),
1984
+ logger::system_data(bootstrap_address_),
1978
1985
  origin_.options().connect_timeout.count());
1979
1986
  connection_deadline_.expires_after(origin_.options().connect_timeout);
1980
1987
  connection_deadline_.async_wait(
@@ -1982,17 +1989,16 @@ private:
1982
1989
  if (timer_ec == asio::error::operation_aborted || self->stopped_) {
1983
1990
  return;
1984
1991
  }
1985
- CB_LOG_DEBUG("{} unable to connect to {}:{} (\"{}:{}\") in time, reconnecting",
1992
+ CB_LOG_DEBUG("{} unable to connect to {} (\"{}\") in time, reconnecting",
1986
1993
  self->log_prefix_,
1987
- hostname,
1988
- port,
1989
- self->bootstrap_hostname_,
1990
- self->bootstrap_port_);
1994
+ logger::system_data(fmt::format("{}:{}", hostname, port)),
1995
+ logger::system_data(self->bootstrap_address_));
1991
1996
  self->initiate_bootstrap();
1992
1997
  });
1993
- stream_->async_connect(it->endpoint(), [capture0 = shared_from_this(), it](auto&& PH1) {
1994
- capture0->on_connect(std::forward<decltype(PH1)>(PH1), it);
1995
- });
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
+ });
1996
2002
  } else {
1997
2003
  auto error_msg =
1998
2004
  fmt::format("no more endpoints left to connect to \"{}:{}\", will try another address",
@@ -2028,10 +2034,10 @@ private:
2028
2034
  ? ERR_error_string(static_cast<unsigned long>(ec.value()), nullptr)
2029
2035
  : ec.message();
2030
2036
  #endif
2031
- CB_LOG_WARNING("{} unable to connect to {}:{}: {} ({}){}. is_open={}",
2037
+ CB_LOG_WARNING("{} unable to connect to {}: {} ({}){}. is_open={}",
2032
2038
  log_prefix_,
2033
- it->endpoint().address().to_string(),
2034
- it->endpoint().port(),
2039
+ logger::system_data(fmt::format(
2040
+ "{}:{}", it->endpoint().address().to_string(), it->endpoint().port())),
2035
2041
  ec.value(),
2036
2042
  error_message,
2037
2043
  (ec == asio::error::connection_refused)
@@ -2042,10 +2048,11 @@ private:
2042
2048
  stream_->close([self = shared_from_this(), next_address = ++it](std::error_code ec) {
2043
2049
  if (ec) {
2044
2050
  CB_LOG_WARNING(
2045
- "{} unable to close socket, but continue connecting attempt to {}:{}: {}",
2051
+ "{} unable to close socket, but continue connecting attempt to {}: {}",
2046
2052
  self->log_prefix_,
2047
- next_address->endpoint().address().to_string(),
2048
- next_address->endpoint().port(),
2053
+ logger::system_data(fmt::format("{}:{}",
2054
+ next_address->endpoint().address().to_string(),
2055
+ next_address->endpoint().port())),
2049
2056
  ec.value());
2050
2057
  }
2051
2058
  self->do_connect(next_address);
@@ -2056,24 +2063,31 @@ private:
2056
2063
  } else {
2057
2064
  stream_->set_options();
2058
2065
  connection_endpoints_ = { it->endpoint(), stream_->local_endpoint() };
2059
- CB_LOG_DEBUG("{} connected to {}:{}:{}",
2066
+ CB_LOG_DEBUG("{} connected to {}",
2060
2067
  log_prefix_,
2061
- connection_endpoints_.local.port(),
2062
- connection_endpoints_.remote_address,
2063
- connection_endpoints_.remote.port());
2064
- log_prefix_ = fmt::format("[{}/{}/{}/{}] <{}:{}/{}:{}>",
2065
- client_id_,
2066
- id_,
2067
- stream_->log_prefix(),
2068
- bucket_name_.value_or("-"),
2069
- connection_endpoints_.local.port(),
2070
- bootstrap_hostname_,
2071
- connection_endpoints_.remote_address,
2072
- connection_endpoints_.remote.port());
2073
- parser_.reset();
2068
+ logger::system_data(fmt::format("{}:{}:{}",
2069
+ connection_endpoints_.local.port(),
2070
+ connection_endpoints_.remote_address,
2071
+ connection_endpoints_.remote.port())));
2074
2072
  {
2075
- const std::scoped_lock lock(output_buffer_mutex_);
2076
- 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;
2077
2091
  }
2078
2092
  bootstrap_handler_ = std::make_shared<bootstrap_handler>(shared_from_this());
2079
2093
 
@@ -2082,14 +2096,13 @@ private:
2082
2096
  if (timer_ec == asio::error::operation_aborted || self->stopped_) {
2083
2097
  return;
2084
2098
  }
2085
- CB_LOG_DEBUG(
2086
- "{} unable to boostrap single node at {}:{}:{} (\"{}:{}\") in time, reconnecting",
2087
- self->log_prefix_,
2088
- self->connection_endpoints_.local.port(),
2089
- self->connection_endpoints_.remote_address,
2090
- self->connection_endpoints_.remote.port(),
2091
- self->bootstrap_hostname_,
2092
- 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_));
2093
2106
  return self->initiate_bootstrap();
2094
2107
  });
2095
2108
  }
@@ -2151,6 +2164,10 @@ private:
2151
2164
 
2152
2165
  for (;;) {
2153
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;
2154
2171
  switch (self->parser_.next(msg)) {
2155
2172
  case mcbp_parser::result::ok: {
2156
2173
  if (self->stopped_) {
@@ -2158,16 +2175,26 @@ private:
2158
2175
  }
2159
2176
  CB_LOG_TRACE(
2160
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).
2161
2183
  if (self->bootstrapped_) {
2162
- self->handler_->handle(std::move(msg));
2163
- } else if (self->bootstrap_handler_) {
2164
- 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));
2165
2189
  }
2166
2190
  if (self->stopped_) {
2167
2191
  return;
2168
2192
  }
2169
2193
  } break;
2170
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));
2171
2198
  self->reading_ = false;
2172
2199
  if (!self->stopped_ && self->stream_->is_open()) {
2173
2200
  self->do_read();
@@ -2185,14 +2212,12 @@ private:
2185
2212
  if (stopped_ || !stream_->is_open()) {
2186
2213
  return;
2187
2214
  }
2188
- const std::scoped_lock lock(writing_buffer_mutex_, output_buffer_mutex_);
2189
- if (!writing_buffer_.empty() || output_buffer_.empty()) {
2215
+ if (!output_queue_.begin_writing()) {
2190
2216
  return;
2191
2217
  }
2192
- std::swap(writing_buffer_, output_buffer_);
2193
2218
  std::vector<asio::const_buffer> buffers;
2194
- buffers.reserve(writing_buffer_.size());
2195
- for (auto& buf : writing_buffer_) {
2219
+ buffers.reserve(output_queue_.writing().size());
2220
+ for (const auto& buf : output_queue_.writing()) {
2196
2221
  CB_LOG_PROTOCOL("[MCBP, OUT] host=\"{}\", sport={}, dport={}, buffer_size={}{:a}",
2197
2222
  connection_endpoints_.remote_address,
2198
2223
  connection_endpoints_.local.port(),
@@ -2222,10 +2247,7 @@ private:
2222
2247
  ec.message());
2223
2248
  return self->stop(retry_reason::socket_closed_while_in_flight);
2224
2249
  }
2225
- {
2226
- const std::scoped_lock inner_lock(self->writing_buffer_mutex_);
2227
- self->writing_buffer_.clear();
2228
- }
2250
+ self->output_queue_.finish_writing();
2229
2251
  asio::post(asio::bind_executor(self->ctx_, [self]() {
2230
2252
  self->do_write();
2231
2253
  self->do_read();
@@ -2235,6 +2257,7 @@ private:
2235
2257
 
2236
2258
  const std::string client_id_;
2237
2259
  std::string node_uuid_;
2260
+ app_telemetry_recorder_cache app_telemetry_recorder_cache_{};
2238
2261
  const std::string id_{ uuid::to_string(uuid::random()) };
2239
2262
  asio::io_context& ctx_;
2240
2263
  asio::ip::tcp::resolver resolver_;
@@ -2254,7 +2277,7 @@ private:
2254
2277
  utils::movable_function<void(std::error_code, const topology::configuration&)>
2255
2278
  bootstrap_callback_{};
2256
2279
  std::mutex command_handlers_mutex_{};
2257
- std::map<std::uint32_t, command_handler> command_handlers_{};
2280
+ opaque_ring_table<command_handler> command_handlers_{};
2258
2281
  std::vector<std::shared_ptr<config_listener>> config_listeners_{};
2259
2282
  utils::movable_function<void()> on_stop_handler_{};
2260
2283
 
@@ -2269,18 +2292,16 @@ private:
2269
2292
  std::atomic<std::uint32_t> opaque_{ 0 };
2270
2293
 
2271
2294
  std::array<std::byte, 16384> input_buffer_{};
2272
- std::vector<std::vector<std::byte>> output_buffer_{};
2295
+ mcbp_output_queue output_queue_{};
2273
2296
  std::vector<std::vector<std::byte>> pending_buffer_{};
2274
- std::vector<std::vector<std::byte>> writing_buffer_{};
2275
- std::mutex output_buffer_mutex_{};
2276
2297
  std::mutex pending_buffer_mutex_{};
2277
- std::mutex writing_buffer_mutex_{};
2278
2298
  std::string bootstrap_hostname_{};
2279
2299
  std::string bootstrap_port_{};
2280
2300
  std::string bootstrap_address_{};
2281
2301
  std::uint16_t bootstrap_port_number_{};
2282
2302
  connection_endpoints connection_endpoints_{ {}, {} };
2283
2303
  asio::ip::tcp::resolver::results_type endpoints_;
2304
+ mutable std::mutex session_info_mutex_{};
2284
2305
  std::vector<protocol::hello_feature> supported_features_;
2285
2306
  std::optional<topology::configuration> config_;
2286
2307
  mutable std::mutex config_mutex_{};
@@ -2306,11 +2327,6 @@ private:
2306
2327
  std::string log_prefix_{};
2307
2328
  std::chrono::time_point<std::chrono::steady_clock> last_active_{};
2308
2329
  std::atomic<diag::endpoint_state> state_{ diag::endpoint_state::disconnected };
2309
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
2310
- std::shared_ptr<columnar::background_bootstrap_listener> background_bootstrap_listener_{
2311
- nullptr
2312
- };
2313
- #endif
2314
2330
  };
2315
2331
 
2316
2332
  mcbp_session::mcbp_session(const std::string& client_id,
@@ -2349,8 +2365,13 @@ mcbp_session::mcbp_session(const std::string& client_id,
2349
2365
  {
2350
2366
  }
2351
2367
 
2368
+ mcbp_session::mcbp_session(std::shared_ptr<mcbp_session_impl> impl)
2369
+ : impl_{ std::move(impl) }
2370
+ {
2371
+ }
2372
+
2352
2373
  auto
2353
- mcbp_session::log_prefix() const -> const std::string&
2374
+ mcbp_session::log_prefix() const -> std::string
2354
2375
  {
2355
2376
  return impl_->log_prefix();
2356
2377
  }
@@ -2409,6 +2430,14 @@ mcbp_session::node_uuid() const -> const std::string&
2409
2430
  return impl_->node_uuid();
2410
2431
  }
2411
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
+
2412
2441
  auto
2413
2442
  mcbp_session::bootstrap_address() const -> const std::string&
2414
2443
  {
@@ -2457,18 +2486,6 @@ mcbp_session::bootstrap_port_number() const -> std::uint16_t
2457
2486
  return impl_->bootstrap_port_number();
2458
2487
  }
2459
2488
 
2460
- auto
2461
- mcbp_session::last_bootstrap_error() && -> std::optional<impl::bootstrap_error>
2462
- {
2463
- return std::move(impl_)->last_bootstrap_error();
2464
- }
2465
-
2466
- auto
2467
- mcbp_session::last_bootstrap_error() const& -> const std::optional<impl::bootstrap_error>&
2468
- {
2469
- return impl_->last_bootstrap_error();
2470
- }
2471
-
2472
2489
  auto
2473
2490
  mcbp_session::canonical_hostname() const -> const std::string&
2474
2491
  {
@@ -2601,13 +2618,4 @@ mcbp_session::write_and_flush(std::vector<std::byte>&& buffer)
2601
2618
  return impl_->write_and_flush(std::move(buffer));
2602
2619
  }
2603
2620
 
2604
- #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
2605
- void
2606
- mcbp_session::add_background_bootstrap_listener(
2607
- std::shared_ptr<columnar::background_bootstrap_listener> listener)
2608
- {
2609
- return impl_->add_background_bootstrap_listener(std::move(listener));
2610
- }
2611
- #endif
2612
-
2613
2621
  } // namespace couchbase::core::io