couchbase 3.7.0-x86_64-linux → 3.8.1-x86_64-linux

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/lib/active_support/cache/couchbase_store.rb +6 -6
  4. data/lib/couchbase/3.2/libcouchbase.so +0 -0
  5. data/lib/couchbase/3.3/libcouchbase.so +0 -0
  6. data/lib/couchbase/3.4/libcouchbase.so +0 -0
  7. data/lib/couchbase/{3.1 → 4.0}/libcouchbase.so +0 -0
  8. data/lib/couchbase/authenticator.rb +14 -0
  9. data/lib/couchbase/binary_collection.rb +37 -22
  10. data/lib/couchbase/bucket.rb +46 -31
  11. data/lib/couchbase/cluster.rb +146 -61
  12. data/lib/couchbase/collection.rb +257 -186
  13. data/lib/couchbase/datastructures/couchbase_list.rb +82 -50
  14. data/lib/couchbase/datastructures/couchbase_map.rb +87 -50
  15. data/lib/couchbase/datastructures/couchbase_queue.rb +65 -38
  16. data/lib/couchbase/datastructures/couchbase_set.rb +58 -41
  17. data/lib/couchbase/deprecations.rb +1 -1
  18. data/lib/couchbase/diagnostics.rb +8 -8
  19. data/lib/couchbase/errors.rb +7 -1
  20. data/lib/couchbase/json_transcoder.rb +1 -1
  21. data/lib/couchbase/libcouchbase.rb +1 -1
  22. data/lib/couchbase/management/analytics_index_manager.rb +90 -59
  23. data/lib/couchbase/management/bucket_manager.rb +73 -45
  24. data/lib/couchbase/management/collection_manager.rb +86 -43
  25. data/lib/couchbase/management/collection_query_index_manager.rb +56 -33
  26. data/lib/couchbase/management/query_index_manager.rb +88 -36
  27. data/lib/couchbase/management/scope_search_index_manager.rb +119 -52
  28. data/lib/couchbase/management/search_index_manager.rb +401 -178
  29. data/lib/couchbase/management/user_manager.rb +343 -174
  30. data/lib/couchbase/management/view_index_manager.rb +166 -73
  31. data/lib/couchbase/metrics/logging_meter.rb +108 -0
  32. data/lib/couchbase/metrics/logging_value_recorder.rb +50 -0
  33. data/lib/couchbase/metrics/meter.rb +27 -0
  34. data/lib/couchbase/metrics/noop_meter.rb +30 -0
  35. data/lib/couchbase/metrics/noop_value_recorder.rb +27 -0
  36. data/lib/couchbase/metrics/value_recorder.rb +25 -0
  37. data/lib/couchbase/options.rb +71 -5
  38. data/lib/couchbase/protostellar/client.rb +0 -2
  39. data/lib/couchbase/protostellar/cluster.rb +7 -0
  40. data/lib/couchbase/protostellar/generated/admin/analytics/v1/analytics_pb.rb +54 -0
  41. data/lib/couchbase/protostellar/generated/admin/analytics/v1/analytics_services_pb.rb +51 -0
  42. data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_pb.rb +5 -24
  43. data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_services_pb.rb +16 -0
  44. data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_pb.rb +5 -24
  45. data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_services_pb.rb +16 -0
  46. data/lib/couchbase/protostellar/generated/admin/query/v1/query_pb.rb +5 -24
  47. data/lib/couchbase/protostellar/generated/admin/query/v1/query_services_pb.rb +18 -0
  48. data/lib/couchbase/protostellar/generated/admin/search/v1/search_pb.rb +2 -23
  49. data/lib/couchbase/protostellar/generated/admin/search/v1/search_services_pb.rb +23 -0
  50. data/lib/couchbase/protostellar/generated/analytics/v1/analytics_pb.rb +4 -25
  51. data/lib/couchbase/protostellar/generated/analytics/v1/analytics_services_pb.rb +10 -0
  52. data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_pb.rb +6 -25
  53. data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_services_pb.rb +18 -0
  54. data/lib/couchbase/protostellar/generated/internal/xdcr/v1/xdcr_pb.rb +46 -0
  55. data/lib/couchbase/protostellar/generated/internal/xdcr/v1/xdcr_services_pb.rb +56 -0
  56. data/lib/couchbase/protostellar/generated/kv/v1/kv_pb.rb +3 -26
  57. data/lib/couchbase/protostellar/generated/kv/v1/kv_services_pb.rb +47 -0
  58. data/lib/couchbase/protostellar/generated/query/v1/query_pb.rb +4 -26
  59. data/lib/couchbase/protostellar/generated/query/v1/query_services_pb.rb +10 -0
  60. data/lib/couchbase/protostellar/generated/routing/v2/routing_pb.rb +26 -0
  61. data/lib/couchbase/protostellar/generated/routing/v2/routing_services_pb.rb +43 -0
  62. data/lib/couchbase/protostellar/generated/search/v1/search_pb.rb +5 -26
  63. data/lib/couchbase/protostellar/generated/search/v1/search_services_pb.rb +11 -0
  64. data/lib/couchbase/protostellar/generated/transactions/v1/transactions_pb.rb +2 -23
  65. data/lib/couchbase/protostellar/generated/transactions/v1/transactions_services_pb.rb +30 -0
  66. data/lib/couchbase/protostellar/generated/view/v1/view_pb.rb +2 -23
  67. data/lib/couchbase/protostellar/generated/view/v1/view_services_pb.rb +9 -0
  68. data/lib/couchbase/protostellar/request_generator/admin/collection.rb +4 -2
  69. data/lib/couchbase/protostellar/request_generator/admin/query.rb +2 -0
  70. data/lib/couchbase/protostellar/request_generator/kv.rb +1 -1
  71. data/lib/couchbase/protostellar/scope.rb +4 -0
  72. data/lib/couchbase/scope.rb +62 -48
  73. data/lib/couchbase/search_options.rb +18 -18
  74. data/lib/couchbase/tracing/noop_span.rb +29 -0
  75. data/lib/couchbase/tracing/noop_tracer.rb +29 -0
  76. data/lib/couchbase/tracing/request_span.rb +34 -0
  77. data/lib/couchbase/tracing/request_tracer.rb +28 -0
  78. data/lib/couchbase/tracing/threshold_logging_span.rb +112 -0
  79. data/lib/couchbase/tracing/threshold_logging_tracer.rb +231 -0
  80. data/lib/couchbase/utils/hdr_histogram.rb +55 -0
  81. data/lib/couchbase/utils/observability.rb +263 -0
  82. data/lib/couchbase/utils/observability_constants.rb +200 -0
  83. data/lib/couchbase/utils/stdlib_logger_adapter.rb +1 -3
  84. data/lib/couchbase/version.rb +1 -1
  85. data/lib/couchbase.rb +2 -2
  86. metadata +43 -10
  87. data/lib/couchbase/protostellar/generated/routing/v1/routing_pb.rb +0 -52
  88. data/lib/couchbase/protostellar/generated/routing/v1/routing_services_pb.rb +0 -30
@@ -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,263 @@
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
+ require "couchbase/tracing/noop_tracer"
20
+ require "couchbase/metrics/noop_meter"
21
+
22
+ require_relative "observability_constants"
23
+
24
+ module Couchbase
25
+ module Observability
26
+ class ClusterLabelListener
27
+ attr_reader :cluster_name
28
+ attr_reader :cluster_uuid
29
+ end
30
+
31
+ class Wrapper
32
+ attr_accessor :tracer
33
+ attr_accessor :meter
34
+
35
+ def initialize(backend: nil, tracer: nil, meter: nil)
36
+ @backend = backend
37
+ @tracer = tracer
38
+ @meter = meter
39
+
40
+ yield self if block_given?
41
+ end
42
+
43
+ def record_operation(op_name, parent_span, receiver, service = nil)
44
+ handler = Handler.new(@backend, op_name, parent_span, receiver, @tracer, @meter)
45
+ handler.add_operation_name(op_name)
46
+ handler.add_service(service) unless service.nil?
47
+ begin
48
+ res = yield(handler)
49
+ rescue StandardError => e
50
+ handler.add_error(e)
51
+ raise e
52
+ else
53
+ handler.set_success
54
+ ensure
55
+ handler.finish
56
+ end
57
+ res
58
+ end
59
+
60
+ def close
61
+ @tracer&.close
62
+ @meter&.close
63
+ end
64
+ end
65
+
66
+ class Handler
67
+ attr_reader :op_span
68
+
69
+ def initialize(backend, op_name, parent_span, receiver, tracer, meter)
70
+ @tracer = tracer
71
+ @meter = meter
72
+ @tracer = Tracing::NoopTracer.new if @tracer.nil?
73
+ @meter = Metrics::NoopMeter.new if @meter.nil?
74
+
75
+ unless backend.nil?
76
+ cluster_labels = backend.cluster_labels
77
+ @cluster_name = cluster_labels[:cluster_name]
78
+ @cluster_uuid = cluster_labels[:cluster_uuid]
79
+ end
80
+
81
+ @op_span = create_span(op_name, parent_span)
82
+ @meter_attributes = create_meter_attributes
83
+ @start_time = Time.now
84
+ add_operation_name(op_name)
85
+ add_receiver_attributes(receiver)
86
+ end
87
+
88
+ def with_request_encoding_span
89
+ span = create_span(STEP_REQUEST_ENCODING, @op_span)
90
+ begin
91
+ res = yield
92
+ ensure
93
+ span.finish
94
+ end
95
+ res
96
+ end
97
+
98
+ def add_service(service)
99
+ service_str =
100
+ case service
101
+ when :kv
102
+ ATTR_VALUE_SERVICE_KV
103
+ when :query
104
+ ATTR_VALUE_SERVICE_QUERY
105
+ when :analytics
106
+ ATTR_VALUE_SERVICE_ANALYTICS
107
+ when :search
108
+ ATTR_VALUE_SERVICE_SEARCH
109
+ when :management
110
+ ATTR_VALUE_SERVICE_MANAGEMENT
111
+ when :views
112
+ ATTR_VALUE_SERVICE_VIEWS
113
+ end
114
+ @op_span.set_attribute(ATTR_SERVICE, service_str) unless service_str.nil?
115
+ @meter_attributes[ATTR_SERVICE] = service_str unless service_str.nil?
116
+ end
117
+
118
+ def add_operation_name(name)
119
+ @op_span.set_attribute(ATTR_OPERATION_NAME, name)
120
+ @meter_attributes[ATTR_OPERATION_NAME] = name
121
+ end
122
+
123
+ def add_bucket_name(name)
124
+ @op_span.set_attribute(ATTR_BUCKET_NAME, name)
125
+ @meter_attributes[ATTR_BUCKET_NAME] = name
126
+ end
127
+
128
+ def add_scope_name(name)
129
+ @op_span.set_attribute(ATTR_SCOPE_NAME, name)
130
+ @meter_attributes[ATTR_SCOPE_NAME] = name
131
+ end
132
+
133
+ def add_collection_name(name)
134
+ @op_span.set_attribute(ATTR_COLLECTION_NAME, name)
135
+ @meter_attributes[ATTR_COLLECTION_NAME] = name
136
+ end
137
+
138
+ def add_durability_level(level)
139
+ durability_str =
140
+ case level
141
+ when :majority
142
+ ATTR_VALUE_DURABILITY_MAJORITY
143
+ when :majority_and_persist_to_active
144
+ ATTR_VALUE_DURABILITY_MAJORITY_AND_PERSIST_TO_ACTIVE
145
+ when :persist_to_majority
146
+ ATTR_VALUE_DURABILITY_PERSIST_TO_MAJORITY
147
+ end
148
+ @op_span.set_attribute(ATTR_DURABILITY, durability_str) unless durability_str.nil?
149
+ end
150
+
151
+ def add_retries(retries)
152
+ @op_span.set_attribute(ATTR_RETRIES, retries.to_i)
153
+ end
154
+
155
+ def set_success
156
+ @op_span.status = :ok
157
+ end
158
+
159
+ def add_error(error)
160
+ @op_span.status = :error
161
+ @meter_attributes[ATTR_ERROR_TYPE] =
162
+ if error.is_a?(Couchbase::Error::CouchbaseError) || error.is_a?(Couchbase::Error::InvalidArgument)
163
+ error.class.name.split("::").last
164
+ else
165
+ "_OTHER"
166
+ end
167
+ end
168
+
169
+ def add_query_statement(statement, options)
170
+ pos_params = options.instance_variable_get(:@positional_parameters)
171
+ named_params = options.instance_variable_get(:@named_parameters)
172
+
173
+ # The statement attribute is added only if positional or named parameters are in use.
174
+ return if (pos_params.nil? || pos_params.empty?) && (named_params.nil? || named_params.empty?)
175
+
176
+ @op_span.set_attribute(ATTR_QUERY_STATEMENT, statement)
177
+ end
178
+
179
+ def add_spans_from_backend(backend_spans)
180
+ backend_spans.each do |backend_span|
181
+ add_backend_span(backend_span, @op_span)
182
+ end
183
+ end
184
+
185
+ def finish
186
+ @op_span.finish
187
+ duration_us = ((Time.now - @start_time) * 1_000_000).round
188
+ @meter.value_recorder(METER_NAME_OPERATION_DURATION, @meter_attributes).record_value(duration_us)
189
+ end
190
+
191
+ private
192
+
193
+ def add_backend_span(backend_span, parent)
194
+ span = @tracer.request_span(
195
+ backend_span[:name],
196
+ parent: parent,
197
+ start_timestamp: convert_backend_timestamp(backend_span[:start_timestamp]),
198
+ )
199
+ backend_span[:attributes].each do |k, v|
200
+ span.set_attribute(k, v)
201
+ end
202
+ if backend_span.key?(:children)
203
+ backend_span[:children].each do |c|
204
+ add_backend_span(c, span)
205
+ end
206
+ end
207
+ span.finish(end_timestamp: convert_backend_timestamp(backend_span[:end_timestamp]))
208
+ end
209
+
210
+ def convert_backend_timestamp(backend_timestamp)
211
+ Time.at(backend_timestamp / (10**6), backend_timestamp % (10**6))
212
+ end
213
+
214
+ def create_meter_attributes
215
+ attrs = {
216
+ ATTR_SYSTEM_NAME => ATTR_VALUE_SYSTEM_NAME,
217
+ ATTR_RESERVED_UNIT => ATTR_VALUE_RESERVED_UNIT_SECONDS,
218
+ }
219
+ attrs[ATTR_CLUSTER_NAME] = @cluster_name unless @cluster_name.nil?
220
+ attrs[ATTR_CLUSTER_UUID] = @cluster_uuid unless @cluster_uuid.nil?
221
+ attrs
222
+ end
223
+
224
+ def create_span(name, parent, start_timestamp: nil)
225
+ span = @tracer.request_span(name, parent: parent, start_timestamp: start_timestamp)
226
+ span.set_attribute(ATTR_SYSTEM_NAME, ATTR_VALUE_SYSTEM_NAME)
227
+ span.set_attribute(ATTR_CLUSTER_NAME, @cluster_name) unless @cluster_name.nil?
228
+ span.set_attribute(ATTR_CLUSTER_UUID, @cluster_uuid) unless @cluster_uuid.nil?
229
+ span
230
+ end
231
+
232
+ def add_receiver_attributes(receiver)
233
+ if receiver.instance_variable_defined?(:@collection)
234
+ collection = receiver.instance_variable_get(:@collection)
235
+ add_bucket_name(collection.bucket_name)
236
+ add_scope_name(collection.scope_name)
237
+ add_collection_name(collection.name)
238
+ return
239
+ end
240
+
241
+ if receiver.instance_variable_defined?(:@bucket_name)
242
+ add_bucket_name(receiver.instance_variable_get(:@bucket_name))
243
+ elsif receiver.instance_variable_defined?(:@name)
244
+ add_bucket_name(receiver.instance_variable_get(:@name))
245
+ return
246
+ end
247
+
248
+ if receiver.instance_variable_defined?(:@scope_name)
249
+ add_scope_name(receiver.instance_variable_get(:@scope_name))
250
+ elsif receiver.instance_variable_defined?(:@name)
251
+ add_scope_name(receiver.instance_variable_get(:@name))
252
+ return
253
+ end
254
+
255
+ if receiver.instance_variable_defined?(:@collection_name)
256
+ add_collection_name(receiver.instance_variable_get(:@collection_name))
257
+ elsif receiver.instance_variable_defined?(:@name)
258
+ add_collection_name(receiver.instance_variable_get(:@name))
259
+ end
260
+ end
261
+ end
262
+ end
263
+ end