couchbase 3.7.0 → 3.8.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 (286) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/ext/CMakeLists.txt +4 -1
  4. data/ext/cache/extconf_include.rb +4 -3
  5. data/ext/cache/mozilla-ca-bundle.crt +66 -93
  6. data/ext/cache/mozilla-ca-bundle.sha256 +1 -1
  7. data/ext/couchbase/CMakeLists.txt +24 -11
  8. data/ext/couchbase/cmake/APKBUILD.in +17 -1
  9. data/ext/couchbase/cmake/Bundler.cmake +9 -1
  10. data/ext/couchbase/cmake/Cache.cmake +48 -19
  11. data/ext/couchbase/cmake/CompilerOptions.cmake +3 -1
  12. data/ext/couchbase/cmake/OpenSSL.cmake +10 -2
  13. data/ext/couchbase/cmake/Packaging.cmake +48 -8
  14. data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +43 -1
  15. data/ext/couchbase/cmake/build_config.hxx.in +2 -0
  16. data/ext/couchbase/cmake/couchbase-cxx-client.spec.in +18 -0
  17. data/ext/couchbase/cmake/tarball_glob.txt +10 -0
  18. data/ext/couchbase/core/app_telemetry_meter.cxx +1 -0
  19. data/ext/couchbase/core/app_telemetry_reporter.cxx +45 -43
  20. data/ext/couchbase/core/app_telemetry_reporter.hxx +4 -3
  21. data/ext/couchbase/core/bucket.cxx +128 -13
  22. data/ext/couchbase/core/bucket.hxx +12 -2
  23. data/ext/couchbase/core/cluster.cxx +304 -152
  24. data/ext/couchbase/core/cluster.hxx +32 -0
  25. data/ext/couchbase/core/cluster_credentials.cxx +25 -0
  26. data/ext/couchbase/core/cluster_credentials.hxx +5 -0
  27. data/ext/couchbase/core/cluster_label_listener.cxx +72 -0
  28. data/ext/couchbase/core/cluster_label_listener.hxx +46 -0
  29. data/ext/couchbase/core/cluster_options.hxx +4 -0
  30. data/ext/couchbase/core/deprecation_utils.hxx +26 -0
  31. data/ext/couchbase/core/error.hxx +27 -0
  32. data/ext/couchbase/core/free_form_http_request.hxx +0 -2
  33. data/ext/couchbase/core/http_component.cxx +12 -48
  34. data/ext/couchbase/core/impl/analytics.cxx +3 -2
  35. data/ext/couchbase/core/impl/analytics.hxx +2 -1
  36. data/ext/couchbase/core/impl/analytics_index_manager.cxx +249 -137
  37. data/ext/couchbase/core/impl/binary_collection.cxx +134 -58
  38. data/ext/couchbase/core/impl/bucket_manager.cxx +87 -35
  39. data/ext/couchbase/core/impl/collection.cxx +560 -245
  40. data/ext/couchbase/core/impl/collection_manager.cxx +89 -49
  41. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +4 -4
  42. data/ext/couchbase/core/impl/error.cxx +20 -13
  43. data/ext/couchbase/core/impl/error.hxx +15 -10
  44. data/ext/couchbase/core/impl/get_all_replicas.hxx +1 -1
  45. data/ext/couchbase/core/impl/get_any_replica.hxx +2 -1
  46. data/ext/couchbase/core/impl/get_replica.hxx +2 -0
  47. data/ext/couchbase/core/impl/lookup_in_replica.hxx +1 -1
  48. data/ext/couchbase/core/impl/observability_recorder.cxx +161 -0
  49. data/ext/couchbase/core/impl/observability_recorder.hxx +77 -0
  50. data/ext/couchbase/core/impl/observe_seqno.hxx +2 -0
  51. data/ext/couchbase/core/impl/public_bucket.cxx +31 -7
  52. data/ext/couchbase/core/impl/public_cluster.cxx +107 -19
  53. data/ext/couchbase/core/impl/query.cxx +6 -3
  54. data/ext/couchbase/core/impl/query.hxx +3 -1
  55. data/ext/couchbase/core/impl/query_index_manager.cxx +267 -102
  56. data/ext/couchbase/core/impl/scope.cxx +53 -11
  57. data/ext/couchbase/core/impl/search.cxx +8 -4
  58. data/ext/couchbase/core/impl/search.hxx +6 -2
  59. data/ext/couchbase/core/impl/search_index_manager.cxx +131 -41
  60. data/ext/couchbase/core/impl/with_cancellation.hxx +75 -0
  61. data/ext/couchbase/core/io/config_tracker.cxx +9 -9
  62. data/ext/couchbase/core/io/config_tracker.hxx +2 -1
  63. data/ext/couchbase/core/io/http_command.hxx +98 -49
  64. data/ext/couchbase/core/io/http_context.hxx +2 -0
  65. data/ext/couchbase/core/io/http_session.cxx +23 -10
  66. data/ext/couchbase/core/io/http_session.hxx +17 -9
  67. data/ext/couchbase/core/io/http_session_manager.hxx +163 -228
  68. data/ext/couchbase/core/io/http_traits.hxx +0 -7
  69. data/ext/couchbase/core/io/mcbp_command.hxx +123 -44
  70. data/ext/couchbase/core/io/mcbp_session.cxx +251 -26
  71. data/ext/couchbase/core/io/mcbp_session.hxx +9 -1
  72. data/ext/couchbase/core/io/mcbp_traits.hxx +0 -8
  73. data/ext/couchbase/core/io/streams.cxx +3 -3
  74. data/ext/couchbase/core/io/streams.hxx +3 -2
  75. data/ext/couchbase/core/meta/features.hxx +15 -0
  76. data/ext/couchbase/core/meta/version.cxx +13 -0
  77. data/ext/couchbase/core/meta/version.hxx +3 -0
  78. data/ext/couchbase/core/metrics/constants.hxx +23 -0
  79. data/ext/couchbase/core/metrics/logging_meter.cxx +5 -5
  80. data/ext/couchbase/core/metrics/meter_wrapper.cxx +65 -63
  81. data/ext/couchbase/core/metrics/meter_wrapper.hxx +12 -10
  82. data/ext/couchbase/core/operations/document_analytics.hxx +0 -5
  83. data/ext/couchbase/core/operations/document_append.hxx +0 -4
  84. data/ext/couchbase/core/operations/document_decrement.hxx +0 -5
  85. data/ext/couchbase/core/operations/document_exists.hxx +0 -7
  86. data/ext/couchbase/core/operations/document_get.hxx +0 -7
  87. data/ext/couchbase/core/operations/document_get_all_replicas.hxx +77 -27
  88. data/ext/couchbase/core/operations/document_get_and_lock.hxx +0 -9
  89. data/ext/couchbase/core/operations/document_get_and_touch.hxx +0 -9
  90. data/ext/couchbase/core/operations/document_get_any_replica.hxx +83 -2
  91. data/ext/couchbase/core/operations/document_get_projected.hxx +0 -9
  92. data/ext/couchbase/core/operations/document_increment.hxx +0 -5
  93. data/ext/couchbase/core/operations/document_insert.hxx +0 -4
  94. data/ext/couchbase/core/operations/document_lookup_in.hxx +0 -9
  95. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +46 -4
  96. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +121 -43
  97. data/ext/couchbase/core/operations/document_mutate_in.hxx +0 -5
  98. data/ext/couchbase/core/operations/document_prepend.hxx +0 -4
  99. data/ext/couchbase/core/operations/document_query.hxx +0 -4
  100. data/ext/couchbase/core/operations/document_remove.hxx +0 -4
  101. data/ext/couchbase/core/operations/document_replace.hxx +0 -4
  102. data/ext/couchbase/core/operations/document_search.hxx +0 -7
  103. data/ext/couchbase/core/operations/document_touch.hxx +0 -7
  104. data/ext/couchbase/core/operations/document_unlock.hxx +0 -6
  105. data/ext/couchbase/core/operations/document_upsert.hxx +0 -4
  106. data/ext/couchbase/core/operations/document_view.cxx +2 -0
  107. data/ext/couchbase/core/operations/document_view.hxx +10 -13
  108. data/ext/couchbase/core/operations/http_noop.hxx +2 -0
  109. data/ext/couchbase/core/operations/management/analytics_dataset_create.hxx +2 -0
  110. data/ext/couchbase/core/operations/management/analytics_dataset_drop.hxx +2 -0
  111. data/ext/couchbase/core/operations/management/analytics_dataset_get_all.hxx +2 -0
  112. data/ext/couchbase/core/operations/management/analytics_dataverse_create.hxx +2 -0
  113. data/ext/couchbase/core/operations/management/analytics_dataverse_drop.hxx +2 -0
  114. data/ext/couchbase/core/operations/management/analytics_get_pending_mutations.hxx +2 -0
  115. data/ext/couchbase/core/operations/management/analytics_index_create.hxx +2 -0
  116. data/ext/couchbase/core/operations/management/analytics_index_drop.hxx +2 -0
  117. data/ext/couchbase/core/operations/management/analytics_index_get_all.hxx +2 -0
  118. data/ext/couchbase/core/operations/management/analytics_link_connect.hxx +2 -0
  119. data/ext/couchbase/core/operations/management/analytics_link_create.hxx +2 -0
  120. data/ext/couchbase/core/operations/management/analytics_link_disconnect.hxx +2 -0
  121. data/ext/couchbase/core/operations/management/analytics_link_drop.hxx +2 -0
  122. data/ext/couchbase/core/operations/management/analytics_link_get_all.hxx +2 -0
  123. data/ext/couchbase/core/operations/management/analytics_link_replace.hxx +2 -0
  124. data/ext/couchbase/core/operations/management/bucket_create.hxx +2 -0
  125. data/ext/couchbase/core/operations/management/bucket_describe.hxx +2 -0
  126. data/ext/couchbase/core/operations/management/bucket_drop.hxx +2 -0
  127. data/ext/couchbase/core/operations/management/bucket_flush.hxx +2 -0
  128. data/ext/couchbase/core/operations/management/bucket_get.hxx +2 -0
  129. data/ext/couchbase/core/operations/management/bucket_get_all.hxx +2 -0
  130. data/ext/couchbase/core/operations/management/bucket_update.hxx +2 -0
  131. data/ext/couchbase/core/operations/management/change_password.hxx +2 -0
  132. data/ext/couchbase/core/operations/management/cluster_describe.hxx +2 -0
  133. data/ext/couchbase/core/operations/management/cluster_developer_preview_enable.hxx +2 -0
  134. data/ext/couchbase/core/operations/management/collection_create.hxx +2 -0
  135. data/ext/couchbase/core/operations/management/collection_drop.hxx +2 -0
  136. data/ext/couchbase/core/operations/management/collection_update.hxx +2 -0
  137. data/ext/couchbase/core/operations/management/collections_manifest_get.hxx +2 -0
  138. data/ext/couchbase/core/operations/management/error_utils.cxx +4 -1
  139. data/ext/couchbase/core/operations/management/eventing_deploy_function.hxx +2 -0
  140. data/ext/couchbase/core/operations/management/eventing_drop_function.hxx +2 -0
  141. data/ext/couchbase/core/operations/management/eventing_get_all_functions.hxx +2 -0
  142. data/ext/couchbase/core/operations/management/eventing_get_function.hxx +2 -0
  143. data/ext/couchbase/core/operations/management/eventing_get_status.hxx +2 -0
  144. data/ext/couchbase/core/operations/management/eventing_pause_function.hxx +2 -0
  145. data/ext/couchbase/core/operations/management/eventing_resume_function.hxx +2 -0
  146. data/ext/couchbase/core/operations/management/eventing_undeploy_function.hxx +2 -0
  147. data/ext/couchbase/core/operations/management/eventing_upsert_function.hxx +2 -0
  148. data/ext/couchbase/core/operations/management/freeform.hxx +2 -0
  149. data/ext/couchbase/core/operations/management/group_drop.hxx +2 -0
  150. data/ext/couchbase/core/operations/management/group_get.hxx +2 -0
  151. data/ext/couchbase/core/operations/management/group_get_all.hxx +2 -0
  152. data/ext/couchbase/core/operations/management/group_upsert.hxx +2 -0
  153. data/ext/couchbase/core/operations/management/query_index_build.hxx +2 -0
  154. data/ext/couchbase/core/operations/management/query_index_build_deferred.hxx +68 -30
  155. data/ext/couchbase/core/operations/management/query_index_create.hxx +2 -0
  156. data/ext/couchbase/core/operations/management/query_index_drop.hxx +2 -0
  157. data/ext/couchbase/core/operations/management/query_index_get_all.hxx +4 -3
  158. data/ext/couchbase/core/operations/management/query_index_get_all_deferred.hxx +2 -1
  159. data/ext/couchbase/core/operations/management/role_get_all.hxx +2 -0
  160. data/ext/couchbase/core/operations/management/scope_create.hxx +2 -0
  161. data/ext/couchbase/core/operations/management/scope_drop.hxx +2 -0
  162. data/ext/couchbase/core/operations/management/scope_get_all.hxx +2 -0
  163. data/ext/couchbase/core/operations/management/search_get_stats.hxx +2 -0
  164. data/ext/couchbase/core/operations/management/search_index_analyze_document.hxx +2 -0
  165. data/ext/couchbase/core/operations/management/search_index_control_ingest.hxx +2 -0
  166. data/ext/couchbase/core/operations/management/search_index_control_plan_freeze.hxx +2 -0
  167. data/ext/couchbase/core/operations/management/search_index_control_query.hxx +2 -0
  168. data/ext/couchbase/core/operations/management/search_index_drop.hxx +2 -0
  169. data/ext/couchbase/core/operations/management/search_index_get.hxx +2 -0
  170. data/ext/couchbase/core/operations/management/search_index_get_all.hxx +2 -0
  171. data/ext/couchbase/core/operations/management/search_index_get_documents_count.hxx +2 -0
  172. data/ext/couchbase/core/operations/management/search_index_get_stats.hxx +2 -0
  173. data/ext/couchbase/core/operations/management/search_index_upsert.hxx +2 -0
  174. data/ext/couchbase/core/operations/management/user_drop.hxx +2 -0
  175. data/ext/couchbase/core/operations/management/user_get.hxx +2 -0
  176. data/ext/couchbase/core/operations/management/user_get_all.hxx +2 -0
  177. data/ext/couchbase/core/operations/management/user_upsert.hxx +2 -0
  178. data/ext/couchbase/core/operations/management/view_index_drop.hxx +2 -0
  179. data/ext/couchbase/core/operations/management/view_index_get.hxx +2 -0
  180. data/ext/couchbase/core/operations/management/view_index_get_all.hxx +2 -0
  181. data/ext/couchbase/core/operations/management/view_index_upsert.hxx +2 -0
  182. data/ext/couchbase/core/operations/operation_traits.hxx +6 -0
  183. data/ext/couchbase/core/operations.hxx +0 -1
  184. data/ext/couchbase/core/operations_fwd.hxx +8 -0
  185. data/ext/couchbase/core/origin.cxx +67 -12
  186. data/ext/couchbase/core/origin.hxx +13 -8
  187. data/ext/couchbase/core/orphan_reporter.cxx +164 -0
  188. data/ext/couchbase/core/orphan_reporter.hxx +65 -0
  189. data/ext/couchbase/core/sasl/CMakeLists.txt +1 -0
  190. data/ext/couchbase/core/sasl/client.cc +6 -0
  191. data/ext/couchbase/core/sasl/mechanism.cc +2 -1
  192. data/ext/couchbase/core/sasl/mechanism.h +2 -1
  193. data/ext/couchbase/core/sasl/oauthbearer/oauthbearer.cc +41 -0
  194. data/ext/couchbase/core/sasl/oauthbearer/oauthbearer.h +47 -0
  195. data/ext/couchbase/core/tls_context_provider.cxx +44 -0
  196. data/ext/couchbase/core/tls_context_provider.hxx +44 -0
  197. data/ext/couchbase/core/tracing/attribute_helpers.hxx +45 -0
  198. data/ext/couchbase/core/tracing/constants.hxx +148 -68
  199. data/ext/couchbase/core/tracing/threshold_logging_options.hxx +0 -3
  200. data/ext/couchbase/core/tracing/threshold_logging_tracer.cxx +122 -170
  201. data/ext/couchbase/core/tracing/tracer_wrapper.cxx +17 -24
  202. data/ext/couchbase/core/tracing/tracer_wrapper.hxx +8 -10
  203. data/ext/couchbase/core/tracing/wrapper_sdk_tracer.cxx +114 -0
  204. data/ext/couchbase/core/tracing/wrapper_sdk_tracer.hxx +85 -0
  205. data/ext/couchbase/core/transactions/attempt_context_impl.cxx +16 -14
  206. data/ext/couchbase/core/transactions/attempt_context_impl.hxx +4 -4
  207. data/ext/couchbase/core/transactions/transactions.cxx +1 -1
  208. data/ext/couchbase/core/transactions/transactions_cleanup.cxx +1 -2
  209. data/ext/couchbase/core/utils/byteswap.hxx +12 -0
  210. data/ext/couchbase/core/utils/concurrent_fixed_priority_queue.hxx +102 -0
  211. data/ext/couchbase/core/utils/connection_string.cxx +2 -0
  212. data/ext/couchbase/couchbase/certificate_authenticator.hxx +1 -0
  213. data/ext/couchbase/couchbase/cluster.hxx +47 -0
  214. data/ext/couchbase/couchbase/cluster_options.hxx +16 -0
  215. data/ext/couchbase/couchbase/collection.hxx +60 -15
  216. data/ext/couchbase/couchbase/error_codes.hxx +48 -48
  217. data/ext/couchbase/couchbase/jwt_authenticator.hxx +52 -0
  218. data/ext/couchbase/couchbase/metrics/meter.hxx +2 -1
  219. data/ext/couchbase/couchbase/metrics/otel_meter.hxx +75 -80
  220. data/ext/couchbase/couchbase/network_options.hxx +19 -0
  221. data/ext/couchbase/couchbase/password_authenticator.hxx +1 -0
  222. data/ext/couchbase/couchbase/tracing/otel_tracer.hxx +15 -17
  223. data/ext/couchbase/couchbase/tracing/request_span.hxx +2 -2
  224. data/ext/couchbase.cxx +4 -0
  225. data/ext/extconf.rb +1 -0
  226. data/ext/rcb_analytics.cxx +157 -47
  227. data/ext/rcb_backend.cxx +118 -71
  228. data/ext/rcb_buckets.cxx +39 -16
  229. data/ext/rcb_collections.cxx +36 -12
  230. data/ext/rcb_crud.cxx +587 -294
  231. data/ext/rcb_hdr_histogram.cxx +219 -0
  232. data/ext/rcb_hdr_histogram.hxx +28 -0
  233. data/ext/rcb_multi.cxx +142 -59
  234. data/ext/rcb_observability.cxx +132 -0
  235. data/ext/rcb_observability.hxx +49 -0
  236. data/ext/rcb_query.cxx +77 -27
  237. data/ext/rcb_search.cxx +92 -31
  238. data/ext/rcb_users.cxx +69 -26
  239. data/ext/rcb_utils.cxx +91 -0
  240. data/ext/rcb_utils.hxx +141 -168
  241. data/ext/rcb_views.cxx +36 -12
  242. data/lib/active_support/cache/couchbase_store.rb +6 -6
  243. data/lib/couchbase/authenticator.rb +14 -0
  244. data/lib/couchbase/binary_collection.rb +37 -22
  245. data/lib/couchbase/bucket.rb +46 -31
  246. data/lib/couchbase/cluster.rb +146 -61
  247. data/lib/couchbase/collection.rb +257 -186
  248. data/lib/couchbase/datastructures/couchbase_list.rb +81 -50
  249. data/lib/couchbase/datastructures/couchbase_map.rb +86 -50
  250. data/lib/couchbase/datastructures/couchbase_queue.rb +64 -38
  251. data/lib/couchbase/datastructures/couchbase_set.rb +57 -41
  252. data/lib/couchbase/deprecations.rb +1 -1
  253. data/lib/couchbase/diagnostics.rb +8 -8
  254. data/lib/couchbase/errors.rb +6 -0
  255. data/lib/couchbase/management/analytics_index_manager.rb +90 -59
  256. data/lib/couchbase/management/bucket_manager.rb +73 -45
  257. data/lib/couchbase/management/collection_manager.rb +86 -43
  258. data/lib/couchbase/management/collection_query_index_manager.rb +56 -33
  259. data/lib/couchbase/management/query_index_manager.rb +88 -36
  260. data/lib/couchbase/management/scope_search_index_manager.rb +119 -52
  261. data/lib/couchbase/management/search_index_manager.rb +401 -178
  262. data/lib/couchbase/management/user_manager.rb +343 -174
  263. data/lib/couchbase/management/view_index_manager.rb +166 -73
  264. data/lib/couchbase/metrics/logging_meter.rb +108 -0
  265. data/lib/couchbase/metrics/logging_value_recorder.rb +50 -0
  266. data/lib/couchbase/metrics/meter.rb +27 -0
  267. data/lib/couchbase/metrics/noop_meter.rb +30 -0
  268. data/lib/couchbase/metrics/noop_value_recorder.rb +27 -0
  269. data/lib/couchbase/metrics/value_recorder.rb +25 -0
  270. data/lib/couchbase/options.rb +69 -3
  271. data/lib/couchbase/protostellar/cluster.rb +3 -0
  272. data/lib/couchbase/scope.rb +62 -48
  273. data/lib/couchbase/search_options.rb +18 -18
  274. data/lib/couchbase/tracing/noop_span.rb +29 -0
  275. data/lib/couchbase/tracing/noop_tracer.rb +29 -0
  276. data/lib/couchbase/tracing/request_span.rb +34 -0
  277. data/lib/couchbase/tracing/request_tracer.rb +28 -0
  278. data/lib/couchbase/tracing/threshold_logging_span.rb +112 -0
  279. data/lib/couchbase/tracing/threshold_logging_tracer.rb +231 -0
  280. data/lib/couchbase/utils/hdr_histogram.rb +55 -0
  281. data/lib/couchbase/utils/observability.rb +257 -0
  282. data/lib/couchbase/utils/observability_constants.rb +200 -0
  283. data/lib/couchbase/utils/stdlib_logger_adapter.rb +1 -3
  284. data/lib/couchbase/version.rb +1 -1
  285. data/lib/couchbase.rb +2 -2
  286. metadata +58 -6
@@ -0,0 +1,231 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2025-Present Couchbase, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'couchbase/tracing/request_tracer'
18
+ require 'couchbase/tracing/threshold_logging_span'
19
+ require 'couchbase/utils/observability_constants'
20
+ require 'couchbase/logger'
21
+ require 'couchbase/utils/stdlib_logger_adapter'
22
+
23
+ require 'json'
24
+
25
+ module Couchbase
26
+ module Tracing
27
+ class ThresholdLoggingTracer < RequestTracer
28
+ # @api private
29
+ DEFAULT_KV_THRESHOLD = 500 # milliseconds
30
+
31
+ # @api private
32
+ DEFAULT_HTTP_THRESHOLD = 1_000 # milliseconds
33
+
34
+ # @api private
35
+ DEFAULT_SAMPLE_SIZE = 10
36
+
37
+ # @api private
38
+ DEFAULT_EMIT_INTERVAL = 10_000 # milliseconds
39
+
40
+ def initialize(
41
+ emit_interval: nil,
42
+ kv_threshold: nil,
43
+ query_threshold: nil,
44
+ views_threshold: nil,
45
+ search_threshold: nil,
46
+ analytics_threshold: nil,
47
+ management_threshold: nil,
48
+ sample_size: nil
49
+ )
50
+ super()
51
+
52
+ emit_interval = DEFAULT_EMIT_INTERVAL if emit_interval.nil?
53
+ kv_threshold = DEFAULT_KV_THRESHOLD if kv_threshold.nil?
54
+ query_threshold = DEFAULT_HTTP_THRESHOLD if query_threshold.nil?
55
+ views_threshold = DEFAULT_HTTP_THRESHOLD if views_threshold.nil?
56
+ search_threshold = DEFAULT_HTTP_THRESHOLD if search_threshold.nil?
57
+ analytics_threshold = DEFAULT_HTTP_THRESHOLD if analytics_threshold.nil?
58
+ management_threshold = DEFAULT_HTTP_THRESHOLD if management_threshold.nil?
59
+ sample_size = DEFAULT_SAMPLE_SIZE if sample_size.nil?
60
+
61
+ raise ArgumentError, "The sample size for ThresholdLoggingTracer must be positive" unless sample_size.positive?
62
+
63
+ @emit_interval = emit_interval
64
+ @sample_size = sample_size
65
+ @groups = {
66
+ Observability::ATTR_VALUE_SERVICE_KV => Group.new(floor_us: 1000 * kv_threshold, capacity: @sample_size),
67
+ Observability::ATTR_VALUE_SERVICE_QUERY => Group.new(floor_us: 1000 * query_threshold, capacity: @sample_size),
68
+ Observability::ATTR_VALUE_SERVICE_VIEWS => Group.new(floor_us: 1000 * views_threshold, capacity: @sample_size),
69
+ Observability::ATTR_VALUE_SERVICE_SEARCH => Group.new(floor_us: 1000 * search_threshold, capacity: @sample_size),
70
+ Observability::ATTR_VALUE_SERVICE_ANALYTICS => Group.new(floor_us: 1000 * analytics_threshold, capacity: @sample_size),
71
+ Observability::ATTR_VALUE_SERVICE_MANAGEMENT => Group.new(floor_us: 1000 * management_threshold, capacity: @sample_size),
72
+ }
73
+
74
+ # TODO(DC): Find better solution for logging
75
+ @logger = Couchbase.logger || Logger.new($stdout, Utils::StdlibLoggerAdapter.map_spdlog_level(Couchbase.log_level))
76
+ start_reporting_thread
77
+ end
78
+
79
+ def request_span(name, parent: nil, start_timestamp: nil)
80
+ ThresholdLoggingSpan.new(
81
+ name,
82
+ start_timestamp: start_timestamp,
83
+ parent: parent,
84
+ tracer: self,
85
+ )
86
+ end
87
+
88
+ def record_operation(service, item)
89
+ group = @groups[service]
90
+ return if group.nil?
91
+
92
+ group.record_operation(item)
93
+ end
94
+
95
+ def close
96
+ @thread.exit
97
+ end
98
+
99
+ def create_report
100
+ data = {}
101
+ @groups.each do |service, group|
102
+ group_data = group.steal_data
103
+ next if group_data.nil?
104
+
105
+ data[service] = group_data
106
+ end
107
+ data
108
+ end
109
+
110
+ def start_reporting_thread
111
+ @thread = Thread.new do # rubocop:disable ThreadSafety/NewThread
112
+ loop do
113
+ sleep(@emit_interval / 1_000.0)
114
+ report = create_report
115
+
116
+ next if report.empty?
117
+
118
+ begin
119
+ @logger.info("Threshold Logging Report: #{report.to_json}")
120
+ rescue StandardError => e
121
+ @logger.debug("Failed to log threshold logging report: #{e.message}")
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ class Item
128
+ attr_accessor :total_duration_us
129
+ attr_accessor :encode_duration_us
130
+ attr_accessor :last_dispatch_duration_us
131
+ attr_accessor :total_dispatch_duration_us
132
+ attr_accessor :last_server_duration_us
133
+ attr_accessor :total_server_duration_us
134
+ attr_accessor :operation_name
135
+ attr_accessor :last_local_id
136
+ attr_accessor :operation_id
137
+ attr_accessor :last_remote_socket
138
+
139
+ def initialize(
140
+ total_duration_us:,
141
+ encode_duration_us:,
142
+ last_dispatch_duration_us:,
143
+ total_dispatch_duration_us:,
144
+ last_server_duration_us:,
145
+ total_server_duration_us:,
146
+ operation_name:,
147
+ last_local_id:,
148
+ operation_id:,
149
+ last_remote_socket:
150
+ )
151
+ @total_duration_us = total_duration_us
152
+ @encode_duration_us = encode_duration_us
153
+ @last_dispatch_duration_us = last_dispatch_duration_us
154
+ @total_dispatch_duration_us = total_dispatch_duration_us
155
+ @last_server_duration_us = last_server_duration_us
156
+ @total_server_duration_us = total_server_duration_us
157
+ @operation_name = operation_name
158
+ @last_local_id = last_local_id
159
+ @operation_id = operation_id
160
+ @last_remote_socket = last_remote_socket
161
+ end
162
+
163
+ def to_h
164
+ {
165
+ total_duration_us: @total_duration_us,
166
+ encode_duration_us: @encode_duration_us,
167
+ last_dispatch_duration_us: @last_dispatch_duration_us,
168
+ total_dispatch_duration_us: @total_dispatch_duration_us,
169
+ last_server_duration_us: @last_server_duration_us,
170
+ total_server_duration_us: @total_server_duration_us,
171
+ operation_name: @operation_name,
172
+ last_local_id: @last_local_id,
173
+ operation_id: @operation_id,
174
+ last_remote_socket: @last_remote_socket,
175
+ }.compact
176
+ end
177
+ end
178
+
179
+ class Group
180
+ def initialize(capacity:, floor_us:)
181
+ @capacity = capacity
182
+ @floor_us = floor_us
183
+
184
+ @total_count = 0
185
+ @top_requests = []
186
+ @mutex = Mutex.new
187
+ end
188
+
189
+ def record_operation(item)
190
+ return if item.total_duration_us < @floor_us
191
+
192
+ @mutex.synchronize do
193
+ @total_count += 1
194
+
195
+ return if @top_requests.size >= @capacity && item.total_duration_us < @top_requests[-1].total_duration_us
196
+
197
+ idx = @top_requests.bsearch_index do |x|
198
+ item.total_duration_us >= x.total_duration_us
199
+ end
200
+
201
+ # The item is smaller than all existing items. We will insert it at the end.
202
+ idx = @top_requests.size if idx.nil?
203
+
204
+ if @top_requests.size >= @capacity
205
+ # We are at capacity, remove the smallest (last) item
206
+ @top_requests.pop
207
+ end
208
+ @top_requests.insert(idx, item)
209
+ end
210
+ end
211
+
212
+ def steal_data
213
+ top_requests, total_count = @mutex.synchronize do
214
+ top_requests_tmp = @top_requests
215
+ total_count_tmp = @total_count
216
+ @top_requests = []
217
+ @total_count = 0
218
+ [top_requests_tmp, total_count_tmp]
219
+ end
220
+
221
+ return nil if total_count.zero?
222
+
223
+ {
224
+ total_count: total_count,
225
+ top_requests: top_requests.map(&:to_h),
226
+ }
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2025-Present Couchbase, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Couchbase
18
+ module Utils
19
+ class HdrHistogram
20
+ def initialize(
21
+ lowest_discernible_value:,
22
+ highest_trackable_value:,
23
+ significant_figures:,
24
+ percentiles: nil
25
+ )
26
+ @histogram_backend = HdrHistogramC.new(lowest_discernible_value, highest_trackable_value, significant_figures)
27
+ @percentiles = percentiles || [50.0, 90.0, 99.0, 99.9, 100.0]
28
+ end
29
+
30
+ def record_value(value)
31
+ @histogram_backend.record_value(value)
32
+ end
33
+
34
+ def close
35
+ @histogram_backend.close
36
+ end
37
+
38
+ def report_and_reset
39
+ backend_report = @histogram_backend.get_percentiles_and_reset(@percentiles)
40
+ total_count = backend_report[:total_count]
41
+
42
+ return nil if total_count.zero?
43
+
44
+ report = {
45
+ total_count: total_count,
46
+ percentiles_us: {},
47
+ }
48
+ @percentiles.zip(backend_report[:percentiles]).each do |percentile, percentile_value|
49
+ report[:percentiles_us][percentile.to_s] = percentile_value
50
+ end
51
+ report
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,257 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2025-Present Couchbase, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require "couchbase/tracing/request_span"
18
+ require "couchbase/tracing/request_tracer"
19
+
20
+ require_relative "observability_constants"
21
+
22
+ module Couchbase
23
+ module Observability
24
+ class ClusterLabelListener
25
+ attr_reader :cluster_name
26
+ attr_reader :cluster_uuid
27
+ end
28
+
29
+ class Wrapper
30
+ attr_accessor :tracer
31
+ attr_accessor :meter
32
+
33
+ def initialize(backend:, tracer: nil, meter: nil)
34
+ @backend = backend
35
+ @tracer = tracer
36
+ @meter = meter
37
+
38
+ yield self if block_given?
39
+ end
40
+
41
+ def record_operation(op_name, parent_span, receiver, service = nil)
42
+ handler = Handler.new(@backend, op_name, parent_span, receiver, @tracer, @meter)
43
+ handler.add_operation_name(op_name)
44
+ handler.add_service(service) unless service.nil?
45
+ begin
46
+ res = yield(handler)
47
+ rescue StandardError => e
48
+ handler.add_error(e)
49
+ raise e
50
+ else
51
+ handler.set_success
52
+ ensure
53
+ handler.finish
54
+ end
55
+ res
56
+ end
57
+
58
+ def close
59
+ @tracer&.close
60
+ @meter&.close
61
+ end
62
+ end
63
+
64
+ class Handler
65
+ attr_reader :op_span
66
+
67
+ def initialize(backend, op_name, parent_span, receiver, tracer, meter)
68
+ @tracer = tracer
69
+ @meter = meter
70
+
71
+ cluster_labels = backend.cluster_labels
72
+ @cluster_name = cluster_labels[:cluster_name]
73
+ @cluster_uuid = cluster_labels[:cluster_uuid]
74
+
75
+ @op_span = create_span(op_name, parent_span)
76
+ @meter_attributes = create_meter_attributes
77
+ @start_time = Time.now
78
+ add_operation_name(op_name)
79
+ add_receiver_attributes(receiver)
80
+ end
81
+
82
+ def with_request_encoding_span
83
+ span = create_span(STEP_REQUEST_ENCODING, @op_span)
84
+ begin
85
+ res = yield
86
+ ensure
87
+ span.finish
88
+ end
89
+ res
90
+ end
91
+
92
+ def add_service(service)
93
+ service_str =
94
+ case service
95
+ when :kv
96
+ ATTR_VALUE_SERVICE_KV
97
+ when :query
98
+ ATTR_VALUE_SERVICE_QUERY
99
+ when :analytics
100
+ ATTR_VALUE_SERVICE_ANALYTICS
101
+ when :search
102
+ ATTR_VALUE_SERVICE_SEARCH
103
+ when :management
104
+ ATTR_VALUE_SERVICE_MANAGEMENT
105
+ when :views
106
+ ATTR_VALUE_SERVICE_VIEWS
107
+ end
108
+ @op_span.set_attribute(ATTR_SERVICE, service_str) unless service_str.nil?
109
+ @meter_attributes[ATTR_SERVICE] = service_str unless service_str.nil?
110
+ end
111
+
112
+ def add_operation_name(name)
113
+ @op_span.set_attribute(ATTR_OPERATION_NAME, name)
114
+ @meter_attributes[ATTR_OPERATION_NAME] = name
115
+ end
116
+
117
+ def add_bucket_name(name)
118
+ @op_span.set_attribute(ATTR_BUCKET_NAME, name)
119
+ @meter_attributes[ATTR_BUCKET_NAME] = name
120
+ end
121
+
122
+ def add_scope_name(name)
123
+ @op_span.set_attribute(ATTR_SCOPE_NAME, name)
124
+ @meter_attributes[ATTR_SCOPE_NAME] = name
125
+ end
126
+
127
+ def add_collection_name(name)
128
+ @op_span.set_attribute(ATTR_COLLECTION_NAME, name)
129
+ @meter_attributes[ATTR_COLLECTION_NAME] = name
130
+ end
131
+
132
+ def add_durability_level(level)
133
+ durability_str =
134
+ case level
135
+ when :majority
136
+ ATTR_VALUE_DURABILITY_MAJORITY
137
+ when :majority_and_persist_to_active
138
+ ATTR_VALUE_DURABILITY_MAJORITY_AND_PERSIST_TO_ACTIVE
139
+ when :persist_to_majority
140
+ ATTR_VALUE_DURABILITY_PERSIST_TO_MAJORITY
141
+ end
142
+ @op_span.set_attribute(ATTR_DURABILITY, durability_str) unless durability_str.nil?
143
+ end
144
+
145
+ def add_retries(retries)
146
+ @op_span.set_attribute(ATTR_RETRIES, retries.to_i)
147
+ end
148
+
149
+ def set_success
150
+ @op_span.status = :ok
151
+ end
152
+
153
+ def add_error(error)
154
+ @op_span.status = :error
155
+ @meter_attributes[ATTR_ERROR_TYPE] =
156
+ if error.is_a?(Couchbase::Error::CouchbaseError) || error.is_a?(Couchbase::Error::InvalidArgument)
157
+ error.class.name.split("::").last
158
+ else
159
+ "_OTHER"
160
+ end
161
+ end
162
+
163
+ def add_query_statement(statement, options)
164
+ pos_params = options.instance_variable_get(:@positional_parameters)
165
+ named_params = options.instance_variable_get(:@named_parameters)
166
+
167
+ # The statement attribute is added only if positional or named parameters are in use.
168
+ return if (pos_params.nil? || pos_params.empty?) && (named_params.nil? || named_params.empty?)
169
+
170
+ @op_span.set_attribute(ATTR_QUERY_STATEMENT, statement)
171
+ end
172
+
173
+ def add_spans_from_backend(backend_spans)
174
+ backend_spans.each do |backend_span|
175
+ add_backend_span(backend_span, @op_span)
176
+ end
177
+ end
178
+
179
+ def finish
180
+ @op_span.finish
181
+ duration_us = ((Time.now - @start_time) * 1_000_000).round
182
+ @meter.value_recorder(METER_NAME_OPERATION_DURATION, @meter_attributes).record_value(duration_us)
183
+ end
184
+
185
+ private
186
+
187
+ def add_backend_span(backend_span, parent)
188
+ span = @tracer.request_span(
189
+ backend_span[:name],
190
+ parent: parent,
191
+ start_timestamp: convert_backend_timestamp(backend_span[:start_timestamp]),
192
+ )
193
+ backend_span[:attributes].each do |k, v|
194
+ span.set_attribute(k, v)
195
+ end
196
+ if backend_span.key?(:children)
197
+ backend_span[:children].each do |c|
198
+ add_backend_span(c, span)
199
+ end
200
+ end
201
+ span.finish(end_timestamp: convert_backend_timestamp(backend_span[:end_timestamp]))
202
+ end
203
+
204
+ def convert_backend_timestamp(backend_timestamp)
205
+ Time.at(backend_timestamp / (10**6), backend_timestamp % (10**6))
206
+ end
207
+
208
+ def create_meter_attributes
209
+ attrs = {
210
+ ATTR_SYSTEM_NAME => ATTR_VALUE_SYSTEM_NAME,
211
+ ATTR_RESERVED_UNIT => ATTR_VALUE_RESERVED_UNIT_SECONDS,
212
+ }
213
+ attrs[ATTR_CLUSTER_NAME] = @cluster_name unless @cluster_name.nil?
214
+ attrs[ATTR_CLUSTER_UUID] = @cluster_uuid unless @cluster_uuid.nil?
215
+ attrs
216
+ end
217
+
218
+ def create_span(name, parent, start_timestamp: nil)
219
+ span = @tracer.request_span(name, parent: parent, start_timestamp: start_timestamp)
220
+ span.set_attribute(ATTR_SYSTEM_NAME, ATTR_VALUE_SYSTEM_NAME)
221
+ span.set_attribute(ATTR_CLUSTER_NAME, @cluster_name) unless @cluster_name.nil?
222
+ span.set_attribute(ATTR_CLUSTER_UUID, @cluster_uuid) unless @cluster_uuid.nil?
223
+ span
224
+ end
225
+
226
+ def add_receiver_attributes(receiver)
227
+ if receiver.instance_variable_defined?(:@collection)
228
+ collection = receiver.instance_variable_get(:@collection)
229
+ add_bucket_name(collection.bucket_name)
230
+ add_scope_name(collection.scope_name)
231
+ add_collection_name(collection.name)
232
+ return
233
+ end
234
+
235
+ if receiver.instance_variable_defined?(:@bucket_name)
236
+ add_bucket_name(receiver.instance_variable_get(:@bucket_name))
237
+ elsif receiver.instance_variable_defined?(:@name)
238
+ add_bucket_name(receiver.instance_variable_get(:@name))
239
+ return
240
+ end
241
+
242
+ if receiver.instance_variable_defined?(:@scope_name)
243
+ add_scope_name(receiver.instance_variable_get(:@scope_name))
244
+ elsif receiver.instance_variable_defined?(:@name)
245
+ add_scope_name(receiver.instance_variable_get(:@name))
246
+ return
247
+ end
248
+
249
+ if receiver.instance_variable_defined?(:@collection_name)
250
+ add_collection_name(receiver.instance_variable_get(:@collection_name))
251
+ elsif receiver.instance_variable_defined?(:@name)
252
+ add_collection_name(receiver.instance_variable_get(:@name))
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end