Skip to content

Commit 3b09513

Browse files
committed
RCBC-536: Add cluster name and cluster uuid span/metric attributes
1 parent 0f464c7 commit 3b09513

File tree

11 files changed

+136
-20
lines changed

11 files changed

+136
-20
lines changed

ext/couchbase.cxx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "rcb_hdr_histogram.hxx"
2929
#include "rcb_logger.hxx"
3030
#include "rcb_multi.hxx"
31+
#include "rcb_observability.hxx"
3132
#include "rcb_query.hxx"
3233
#include "rcb_range_scan.hxx"
3334
#include "rcb_search.hxx"
@@ -66,5 +67,6 @@ Init_libcouchbase(void)
6667
couchbase::ruby::init_extras(cBackend);
6768
couchbase::ruby::init_logger_methods(cBackend);
6869
couchbase::ruby::init_hdr_histogram(mCouchbase);
70+
couchbase::ruby::init_observability(cBackend);
6971
}
7072
}

ext/rcb_observability.cxx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
* limitations under the License.
1616
*/
1717

18+
#include "rcb_backend.hxx"
1819
#include "rcb_utils.hxx"
1920

21+
#include <core/cluster.hxx>
22+
#include <core/cluster_label_listener.hxx>
2023
#include <core/tracing/wrapper_sdk_tracer.hxx>
2124

2225
#include <ruby.h>
@@ -76,4 +79,38 @@ cb_add_core_spans(VALUE observability_handler,
7679
rb_funcall(observability_handler, add_retries_func, ULONG2NUM(retry_attempts));
7780
}
7881
}
82+
83+
namespace
84+
{
85+
VALUE
86+
cb_Backend_cluster_labels(VALUE self)
87+
{
88+
VALUE res = rb_hash_new();
89+
{
90+
auto cluster = cb_backend_to_core_api_cluster(self);
91+
auto labels = cluster.cluster_label_listener()->cluster_labels();
92+
93+
static const auto sym_cluster_name = rb_id2sym(rb_intern("cluster_name"));
94+
static const auto sym_cluster_uuid = rb_id2sym(rb_intern("cluster_uuid"));
95+
96+
if (labels.cluster_name.has_value()) {
97+
rb_hash_aset(res, sym_cluster_name, cb_str_new(labels.cluster_name.value()));
98+
} else {
99+
rb_hash_aset(res, sym_cluster_name, Qnil);
100+
}
101+
if (labels.cluster_uuid.has_value()) {
102+
rb_hash_aset(res, sym_cluster_uuid, cb_str_new(labels.cluster_uuid.value()));
103+
} else {
104+
rb_hash_aset(res, sym_cluster_uuid, Qnil);
105+
}
106+
}
107+
return res;
108+
}
109+
} // namespace
110+
111+
void
112+
init_observability(VALUE cBackend)
113+
{
114+
rb_define_method(cBackend, "cluster_labels", cb_Backend_cluster_labels, 0);
115+
}
79116
} // namespace couchbase::ruby

ext/rcb_observability.hxx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,7 @@ void
3636
cb_add_core_spans(VALUE observability_handler,
3737
std::shared_ptr<couchbase::core::tracing::wrapper_sdk_span> parent_span,
3838
std::size_t retry_attempts);
39+
40+
void
41+
init_observability(VALUE cBackend);
3942
} // namespace couchbase::ruby

lib/couchbase/cluster.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,9 @@ def initialize(connection_string, *args)
404404
end
405405
end
406406

407-
@observability = Observability::Wrapper.new do |w|
407+
@backend = Backend.new
408+
409+
@observability = Observability::Wrapper.new(backend: @backend) do |w|
408410
w.tracer = if !open_options[:enable_tracing].nil? && !open_options[:enable_tracing]
409411
Tracing::NoopTracer.new
410412
elsif tracer.nil?
@@ -432,7 +434,6 @@ def initialize(connection_string, *args)
432434
end
433435
end
434436

435-
@backend = Backend.new
436437
@backend.open(connection_string, credentials, open_options)
437438
end
438439

lib/couchbase/utils/observability.rb

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,16 @@ class Wrapper
3030
attr_accessor :tracer
3131
attr_accessor :meter
3232

33-
def initialize
33+
def initialize(backend:, tracer: nil, meter: nil)
34+
@backend = backend
35+
@tracer = tracer
36+
@meter = meter
37+
3438
yield self if block_given?
3539
end
3640

3741
def record_operation(op_name, parent_span, receiver, service = nil)
38-
handler = Handler.new(op_name, parent_span, receiver, @tracer, @meter)
42+
handler = Handler.new(@backend, op_name, parent_span, receiver, @tracer, @meter)
3943
handler.add_service(service) unless service.nil?
4044
begin
4145
res = yield(handler)
@@ -57,9 +61,14 @@ def close
5761
class Handler
5862
attr_reader :op_span
5963

60-
def initialize(op_name, parent_span, receiver, tracer, meter)
64+
def initialize(backend, op_name, parent_span, receiver, tracer, meter)
6165
@tracer = tracer
6266
@meter = meter
67+
68+
cluster_labels = backend.cluster_labels
69+
@cluster_name = cluster_labels[:cluster_name]
70+
@cluster_uuid = cluster_labels[:cluster_uuid]
71+
6372
@op_span = create_span(op_name, parent_span)
6473
@meter_attributes = create_meter_attributes
6574
@start_time = Time.now
@@ -195,14 +204,19 @@ def convert_backend_timestamp(backend_timestamp)
195204
end
196205

197206
def create_meter_attributes
198-
{
207+
attrs = {
199208
ATTR_SYSTEM_NAME => ATTR_VALUE_SYSTEM_NAME,
200209
}
210+
attrs[ATTR_CLUSTER_NAME] = @cluster_name unless @cluster_name.nil?
211+
attrs[ATTR_CLUSTER_UUID] = @cluster_uuid unless @cluster_uuid.nil?
212+
attrs
201213
end
202214

203215
def create_span(name, parent, start_timestamp: nil)
204216
span = @tracer.request_span(name, parent: parent, start_timestamp: start_timestamp)
205217
span.set_attribute(ATTR_SYSTEM_NAME, ATTR_VALUE_SYSTEM_NAME)
218+
span.set_attribute(ATTR_CLUSTER_NAME, @cluster_name) unless @cluster_name.nil?
219+
span.set_attribute(ATTR_CLUSTER_UUID, @cluster_uuid) unless @cluster_uuid.nil?
206220
span
207221
end
208222

test/metrics_test.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def test_get_and_replace
4343
end
4444

4545
assert_operation_metrics(
46+
env,
4647
10,
4748
operation_name: "get",
4849
service: "kv",
@@ -51,6 +52,7 @@ def test_get_and_replace
5152
collection_name: "_default",
5253
)
5354
assert_operation_metrics(
55+
env,
5456
10,
5557
operation_name: "replace",
5658
service: "kv",
@@ -66,6 +68,7 @@ def test_get_document_not_found
6668
end
6769

6870
assert_operation_metrics(
71+
env,
6972
1,
7073
operation_name: "get",
7174
service: "kv",
@@ -80,6 +83,7 @@ def test_upsert
8083
@collection.upsert(uniq_id(:foo), {foo: "bar"})
8184

8285
assert_operation_metrics(
86+
env,
8387
1,
8488
operation_name: "upsert",
8589
service: "kv",
@@ -95,6 +99,7 @@ def test_cluster_level_query
9599
@cluster.query("SELECT 1=1")
96100

97101
assert_operation_metrics(
102+
env,
98103
1,
99104
operation_name: "query",
100105
service: "query",
@@ -107,6 +112,7 @@ def test_scope_level_query
107112
@bucket.default_scope.query("SELECT 1=1")
108113

109114
assert_operation_metrics(
115+
env,
110116
1,
111117
operation_name: "query",
112118
service: "query",
@@ -123,6 +129,7 @@ def test_query_parsing_failure
123129
end
124130

125131
assert_operation_metrics(
132+
env,
126133
1,
127134
operation_name: "query",
128135
service: "query",

test/query_index_manager_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ def test_get_all_indexes
6767

6868
assert_equal 1, get_all_indexes_spans.size
6969
assert_http_span(
70+
env,
7071
get_all_indexes_spans.first,
7172
"manager_query_get_all_indexes",
7273
parent: @parent_span,
@@ -79,6 +80,7 @@ def test_get_all_indexes
7980
assert_equal 2, create_index_spans.size
8081
create_index_spans.each do |span|
8182
assert_http_span(
83+
env,
8284
span,
8385
"manager_query_create_index",
8486
parent: @parent_span,
@@ -129,6 +131,7 @@ def test_query_indexes
129131
assert_equal 4, get_all_indexes_root_spans.size
130132
get_all_indexes_root_spans.each do |span|
131133
assert_http_span(
134+
env,
132135
span,
133136
"manager_query_get_all_indexes",
134137
parent: @parent_span,
@@ -141,6 +144,7 @@ def test_query_indexes
141144

142145
assert_equal 1, create_primary_index_spans.size
143146
assert_http_span(
147+
env,
144148
create_primary_index_spans.first,
145149
"manager_query_create_primary_index",
146150
parent: @parent_span,
@@ -153,6 +157,7 @@ def test_query_indexes
153157
assert_equal 2, create_index_spans.size
154158
create_index_spans.each do |span|
155159
assert_http_span(
160+
env,
156161
span,
157162
"manager_query_create_index",
158163
parent: @parent_span,
@@ -165,6 +170,7 @@ def test_query_indexes
165170

166171
assert_equal 1, build_deferred_indexes_spans.size
167172
assert_http_span(
173+
env,
168174
build_deferred_indexes_spans.first,
169175
"manager_query_build_deferred_indexes",
170176
parent: @parent_span,
@@ -177,6 +183,7 @@ def test_query_indexes
177183
assert_equal 2, watch_indexes_spans.size
178184
watch_indexes_spans.each do |span|
179185
assert_http_span(
186+
env,
180187
span,
181188
"manager_query_watch_indexes",
182189
parent: @parent_span,
@@ -187,6 +194,7 @@ def test_query_indexes
187194
assert_predicate span.children.size, :positive?
188195
span.children.each do |child_span|
189196
assert_http_span(
197+
env,
190198
child_span,
191199
"manager_query_get_all_indexes",
192200
parent: span,

test/test_helper.rb

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ def supports_multiple_xattr_keys_mutation?
141141
def supports_server_group_replica_reads?
142142
@version >= Gem::Version.create("7.6.2")
143143
end
144+
145+
def supports_cluster_labels?
146+
@version >= Gem::Version.create("7.6.4")
147+
end
144148
end
145149

146150
require "couchbase"
@@ -177,7 +181,7 @@ def bucket
177181
end
178182

179183
def management_endpoint
180-
@management_endpoint = ENV.fetch("TEST_MANAGEMENT_ENDPOINT") do
184+
@management_endpoint ||= ENV.fetch("TEST_MANAGEMENT_ENDPOINT") do
181185
if connection_string
182186
parsed = Couchbase::Backend.parse_connection_string(connection_string)
183187
first_node_address = parsed[:nodes].first[:address]
@@ -216,6 +220,31 @@ def consistency
216220
TestUtilities::MockConsistencyHelper.new
217221
end
218222
end
223+
224+
def cluster_name
225+
fetch_cluster_labels if @cluster_labels.nil?
226+
@cluster_labels[:cluster_name]
227+
end
228+
229+
def cluster_uuid
230+
fetch_cluster_labels if @cluster_labels.nil?
231+
@cluster_labels[:cluster_uuid]
232+
end
233+
234+
private
235+
236+
def fetch_cluster_labels
237+
uri = URI("#{management_endpoint}/pools/default/nodeServices")
238+
req = Net::HTTP::Get.new(uri)
239+
req.basic_auth(username, password)
240+
resp = Net::HTTP.start(uri.hostname, uri.port) { |http| http.request(req) }
241+
body = JSON.parse(resp.body)
242+
243+
@cluster_labels = {
244+
cluster_name: body["clusterName"],
245+
cluster_uuid: body["clusterUUID"],
246+
}
247+
end
219248
end
220249

221250
module TestUtilities

test/tracing_test.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def test_get
4242
spans = @tracer.spans("get")
4343

4444
assert_equal 1, spans.size
45-
assert_kv_span spans[0], "get", parent
45+
assert_kv_span env, spans[0], "get", parent
4646
end
4747

4848
def test_upsert
@@ -53,8 +53,8 @@ def test_upsert
5353
spans = @tracer.spans("upsert")
5454

5555
assert_equal 1, spans.size
56-
assert_kv_span spans[0], "upsert", parent
57-
assert_has_request_encoding_span spans[0]
56+
assert_kv_span env, spans[0], "upsert", parent
57+
assert_has_request_encoding_span env, spans[0]
5858
end
5959

6060
def test_replace
@@ -67,8 +67,8 @@ def test_replace
6767
spans = @tracer.spans("replace")
6868

6969
assert_equal 1, spans.size
70-
assert_kv_span spans[0], "replace", parent
71-
assert_has_request_encoding_span spans[0]
70+
assert_kv_span env, spans[0], "replace", parent
71+
assert_has_request_encoding_span env, spans[0]
7272
end
7373

7474
def test_replace_durable
@@ -84,8 +84,8 @@ def test_replace_durable
8484
spans = @tracer.spans("replace")
8585

8686
assert_equal 1, spans.size
87-
assert_kv_span spans[0], "replace", parent
88-
assert_has_request_encoding_span spans[0]
87+
assert_kv_span env, spans[0], "replace", parent
88+
assert_has_request_encoding_span env, spans[0]
8989
assert_equal "persist_majority", spans[0].attributes["couchbase.durability"]
9090
end
9191
end

test/utils/metrics.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
require_relative "metrics/test_value_recorder"
1919

2020
def assert_operation_metrics(
21+
env,
2122
count,
2223
operation_name:,
2324
service: nil,
@@ -31,6 +32,11 @@ def assert_operation_metrics(
3132
"db.operation.name" => operation_name,
3233
}
3334

35+
if env.server_version.supports_cluster_labels?
36+
attributes["couchbase.cluster.name"] = env.cluster_name
37+
attributes["couchbase.cluster.uuid"] = env.cluster_uuid
38+
end
39+
3440
attributes["couchbase.service"] = service unless service.nil?
3541
attributes["db.namespace"] = bucket_name unless bucket_name.nil?
3642
attributes["couchbase.scope.name"] = scope_name unless scope_name.nil?

0 commit comments

Comments
 (0)