Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ task :cache_cxx_dependencies do
"-DCOUCHBASE_CXX_CLIENT_BUILD_TOOLS=OFF",
"-DCOUCHBASE_CXX_CLIENT_BUILD_DOCS=OFF",
"-DCOUCHBASE_CXX_CLIENT_STATIC_BORINGSSL=ON",
"-DCOUCHBASE_CXX_CLIENT_BUILD_OPENTELEMETRY=OFF",
"-DCPM_DOWNLOAD_ALL=ON",
"-DCPM_USE_NAMED_CACHE_DIRECTORIES=ON",
"-DCPM_USE_LOCAL_PACKAGES=OFF",
Expand Down Expand Up @@ -226,6 +227,7 @@ task :cache_cxx_dependencies do
"-DCPM_USE_LOCAL_PACKAGES=OFF",
"-DCPM_SOURCE_CACHE=#{cpm_cache_dir}",
"-DCOUCHBASE_CXX_CLIENT_EMBED_MOZILLA_CA_BUNDLE_ROOT=#{cpm_cache_dir}",
"-DCOUCHBASE_CXX_CLIENT_BUILD_OPENTELEMETRY=OFF",
]
cmake_flags << "-DCMAKE_C_COMPILER=#{cc}" if cc
cmake_flags << "-DCMAKE_CXX_COMPILER=#{cxx}" if cxx
Expand Down
1 change: 1 addition & 0 deletions couchbase.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,6 @@ Gem::Specification.new do |spec|
spec.extensions = ["ext/extconf.rb"]
spec.rdoc_options << "--exclude" << "ext/"

spec.add_dependency "concurrent-ruby", "~> 1.3"
spec.add_dependency "grpc", "~> 1.59"
end
4 changes: 3 additions & 1 deletion ext/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ add_library(
rcb_utils.cxx
rcb_version.cxx
rcb_views.cxx
rcb_observability.cxx)
rcb_observability.cxx
rcb_hdr_histogram.cxx)
target_include_directories(couchbase PRIVATE ${PROJECT_BINARY_DIR}/generated)
target_include_directories(
couchbase
Expand All @@ -77,6 +78,7 @@ target_link_libraries(
asio
taocpp::json
spdlog::spdlog
hdr_histogram_static
snappy)
if(RUBY_LIBRUBY)
target_link_directories(couchbase PRIVATE "${RUBY_LIBRARY_DIR}")
Expand Down
2 changes: 1 addition & 1 deletion ext/couchbase
4 changes: 4 additions & 0 deletions ext/couchbase.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
#include "rcb_diagnostics.hxx"
#include "rcb_exceptions.hxx"
#include "rcb_extras.hxx"
#include "rcb_hdr_histogram.hxx"
#include "rcb_logger.hxx"
#include "rcb_multi.hxx"
#include "rcb_observability.hxx"
#include "rcb_query.hxx"
#include "rcb_range_scan.hxx"
#include "rcb_search.hxx"
Expand Down Expand Up @@ -64,5 +66,7 @@ Init_libcouchbase(void)
couchbase::ruby::init_diagnostics(cBackend);
couchbase::ruby::init_extras(cBackend);
couchbase::ruby::init_logger_methods(cBackend);
couchbase::ruby::init_hdr_histogram(mCouchbase);
couchbase::ruby::init_observability(cBackend);
}
}
1 change: 1 addition & 0 deletions ext/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def sys(*cmd)
"-DCOUCHBASE_CXX_CLIENT_BUILD_TOOLS=OFF",
"-DCOUCHBASE_CXX_CLIENT_BUILD_EXAMPLES=OFF",
"-DCOUCHBASE_CXX_CLIENT_INSTALL=OFF",
"-DCOUCHBASE_CXX_CLIENT_BUILD_OPENTELEMETRY=OFF",
]

if version.start_with?("4")
Expand Down
10 changes: 1 addition & 9 deletions ext/rcb_backend.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -426,15 +426,7 @@ initialize_cluster_options(const core::utils::connection_string& connstr,
cluster_options.tracing().tracer(
std::make_shared<couchbase::core::tracing::wrapper_sdk_tracer>());

static const auto sym_enable_metrics = rb_id2sym(rb_intern("enable_metrics"));
if (auto param = options::get_bool(options, sym_enable_metrics); param) {
cluster_options.metrics().enable(param.value());
}

static const auto sym_metrics_emit_interval = rb_id2sym(rb_intern("metrics_emit_interval"));
if (auto param = options::get_milliseconds(options, sym_metrics_emit_interval); param) {
cluster_options.metrics().emit_interval(param.value());
}
cluster_options.metrics().enable(false); // Metrics are handled on the wrapper-side

static const auto sym_app_telemetry = rb_id2sym(rb_intern("application_telemetry"));
if (auto app_telemetry_options = options::get_hash(options, sym_app_telemetry);
Expand Down
219 changes: 219 additions & 0 deletions ext/rcb_hdr_histogram.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
* Copyright 2025-Present Couchbase, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "rcb_hdr_histogram.hxx"
#include "rcb_exceptions.hxx"

#include <hdr/hdr_histogram.h>
#include <ruby.h>

#include <mutex>
#include <shared_mutex>
#include <vector>

namespace couchbase::ruby
{
namespace
{
struct cb_hdr_histogram_data {
hdr_histogram* histogram{ nullptr };
std::shared_mutex mutex{};
};

void
cb_hdr_histogram_close(cb_hdr_histogram_data* hdr_histogram_data)
{
if (hdr_histogram_data->histogram != nullptr) {
hdr_close(hdr_histogram_data->histogram);
hdr_histogram_data->histogram = nullptr;
}
}

void
cb_HdrHistogramC_mark(void* /* ptr */)
{
/* no embedded ruby objects -- no mark */
}

void
cb_HdrHistogramC_free(void* ptr)
{
auto* hdr_histogram_data = static_cast<cb_hdr_histogram_data*>(ptr);
cb_hdr_histogram_close(hdr_histogram_data);
hdr_histogram_data->~cb_hdr_histogram_data();
ruby_xfree(hdr_histogram_data);
}

std::size_t
cb_HdrHistogramC_memsize(const void* ptr)
{
const auto* hdr_histogram_data = static_cast<const cb_hdr_histogram_data*>(ptr);
return sizeof(*hdr_histogram_data);
}

const rb_data_type_t cb_hdr_histogram_type{
"Couchbase/Utils/HdrHistogramC",
{
cb_HdrHistogramC_mark,
cb_HdrHistogramC_free,
cb_HdrHistogramC_memsize,
// only one reserved field when GC.compact implemented
#ifdef T_MOVED
nullptr,
#endif
{},
},
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
nullptr,
nullptr,
RUBY_TYPED_FREE_IMMEDIATELY,
#endif
};

VALUE
cb_HdrHistogramC_allocate(VALUE klass)
{
cb_hdr_histogram_data* hdr_histogram = nullptr;
VALUE obj =
TypedData_Make_Struct(klass, cb_hdr_histogram_data, &cb_hdr_histogram_type, hdr_histogram);
new (hdr_histogram) cb_hdr_histogram_data();
return obj;
}

VALUE
cb_HdrHistogramC_initialize(VALUE self,
VALUE lowest_discernible_value,
VALUE highest_trackable_value,
VALUE significant_figures)
{
Check_Type(lowest_discernible_value, T_FIXNUM);
Check_Type(highest_trackable_value, T_FIXNUM);
Check_Type(significant_figures, T_FIXNUM);

std::int64_t lowest = NUM2LL(lowest_discernible_value);
std::int64_t highest = NUM2LL(highest_trackable_value);
int sigfigs = NUM2INT(significant_figures);

cb_hdr_histogram_data* hdr_histogram;
TypedData_Get_Struct(self, cb_hdr_histogram_data, &cb_hdr_histogram_type, hdr_histogram);

int res;
{
const std::unique_lock lock(hdr_histogram->mutex);
res = hdr_init(lowest, highest, sigfigs, &hdr_histogram->histogram);
}
if (res != 0) {
rb_raise(exc_couchbase_error(), "failed to initialize HDR histogram");
return self;
}

return self;
}

VALUE
cb_HdrHistogramC_close(VALUE self)
{
cb_hdr_histogram_data* hdr_histogram;
TypedData_Get_Struct(self, cb_hdr_histogram_data, &cb_hdr_histogram_type, hdr_histogram);
{
const std::unique_lock lock(hdr_histogram->mutex);
cb_hdr_histogram_close(hdr_histogram);
}
return Qnil;
}

VALUE
cb_HdrHistogramC_record_value(VALUE self, VALUE value)
{
Check_Type(value, T_FIXNUM);

std::int64_t val = NUM2LL(value);

cb_hdr_histogram_data* hdr_histogram;
TypedData_Get_Struct(self, cb_hdr_histogram_data, &cb_hdr_histogram_type, hdr_histogram);

{
const std::shared_lock lock(hdr_histogram->mutex);
hdr_record_value_atomic(hdr_histogram->histogram, val);
}
return Qnil;
}

VALUE
cb_HdrHistogramC_get_percentiles_and_reset(VALUE self, VALUE percentiles)
{
Check_Type(percentiles, T_ARRAY);

cb_hdr_histogram_data* hdr_histogram;
TypedData_Get_Struct(self, cb_hdr_histogram_data, &cb_hdr_histogram_type, hdr_histogram);

std::vector<std::int64_t> percentile_values{};
std::int64_t total_count;
{
const std::unique_lock lock(hdr_histogram->mutex);
total_count = hdr_histogram->histogram->total_count;
for (std::size_t i = 0; i < static_cast<std::size_t>(RARRAY_LEN(percentiles)); ++i) {
VALUE entry = rb_ary_entry(percentiles, static_cast<long>(i));
Check_Type(entry, T_FLOAT);
double perc = NUM2DBL(entry);
std::int64_t value_at_perc = hdr_value_at_percentile(hdr_histogram->histogram, perc);
percentile_values.push_back(value_at_perc);
}
hdr_reset(hdr_histogram->histogram);
}

static const VALUE sym_total_count = rb_id2sym(rb_intern("total_count"));
static const VALUE sym_percentiles = rb_id2sym(rb_intern("percentiles"));
VALUE res = rb_hash_new();
rb_hash_aset(res, sym_total_count, LL2NUM(total_count));
VALUE perc_array = rb_ary_new_capa(static_cast<long>(percentile_values.size()));
for (const auto& val : percentile_values) {
rb_ary_push(perc_array, LL2NUM(val));
}
rb_hash_aset(res, sym_percentiles, perc_array);
return res;
}

VALUE
cb_HdrHistogramC_bin_count(VALUE self)
{
cb_hdr_histogram_data* hdr_histogram;
TypedData_Get_Struct(self, cb_hdr_histogram_data, &cb_hdr_histogram_type, hdr_histogram);

std::int32_t bin_count;
{
const std::unique_lock lock(hdr_histogram->mutex);
bin_count = hdr_histogram->histogram->bucket_count;
}
return LONG2NUM(bin_count);
}
} // namespace

void
init_hdr_histogram(VALUE mCouchbase)
{
VALUE mUtils = rb_define_module_under(mCouchbase, "Utils");
VALUE cHdrHistogramC = rb_define_class_under(mUtils, "HdrHistogramC", rb_cObject);
rb_define_alloc_func(cHdrHistogramC, cb_HdrHistogramC_allocate);
rb_define_method(cHdrHistogramC, "initialize", cb_HdrHistogramC_initialize, 3);
rb_define_method(cHdrHistogramC, "close", cb_HdrHistogramC_close, 0);
rb_define_method(cHdrHistogramC, "record_value", cb_HdrHistogramC_record_value, 1);
rb_define_method(cHdrHistogramC, "bin_count", cb_HdrHistogramC_bin_count, 0);
rb_define_method(
cHdrHistogramC, "get_percentiles_and_reset", cb_HdrHistogramC_get_percentiles_and_reset, 1);
}
} // namespace couchbase::ruby
28 changes: 28 additions & 0 deletions ext/rcb_hdr_histogram.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
* Copyright 2025-Present Couchbase, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef COUCHBASE_RUBY_RCB_HDR_HISTOGRAM_HXX
#define COUCHBASE_RUBY_RCB_HDR_HISTOGRAM_HXX

#include <ruby/internal/value.h>

namespace couchbase::ruby
{
void
init_hdr_histogram(VALUE mCouchbase);
} // namespace couchbase::ruby
#endif // COUCHBASE_RUBY_RCB_HDR_HISTOGRAM_HXX
37 changes: 37 additions & 0 deletions ext/rcb_observability.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
* limitations under the License.
*/

#include "rcb_backend.hxx"
#include "rcb_utils.hxx"

#include <core/cluster.hxx>
#include <core/cluster_label_listener.hxx>
#include <core/tracing/wrapper_sdk_tracer.hxx>

#include <ruby.h>
Expand Down Expand Up @@ -76,4 +79,38 @@ cb_add_core_spans(VALUE observability_handler,
rb_funcall(observability_handler, add_retries_func, ULONG2NUM(retry_attempts));
}
}

namespace
{
VALUE
cb_Backend_cluster_labels(VALUE self)
{
VALUE res = rb_hash_new();
{
auto cluster = cb_backend_to_core_api_cluster(self);
auto labels = cluster.cluster_label_listener()->cluster_labels();

static const auto sym_cluster_name = rb_id2sym(rb_intern("cluster_name"));
static const auto sym_cluster_uuid = rb_id2sym(rb_intern("cluster_uuid"));

if (labels.cluster_name.has_value()) {
rb_hash_aset(res, sym_cluster_name, cb_str_new(labels.cluster_name.value()));
} else {
rb_hash_aset(res, sym_cluster_name, Qnil);
}
if (labels.cluster_uuid.has_value()) {
rb_hash_aset(res, sym_cluster_uuid, cb_str_new(labels.cluster_uuid.value()));
} else {
rb_hash_aset(res, sym_cluster_uuid, Qnil);
}
}
return res;
}
} // namespace

void
init_observability(VALUE cBackend)
{
rb_define_method(cBackend, "cluster_labels", cb_Backend_cluster_labels, 0);
}
} // namespace couchbase::ruby
3 changes: 3 additions & 0 deletions ext/rcb_observability.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ void
cb_add_core_spans(VALUE observability_handler,
std::shared_ptr<couchbase::core::tracing::wrapper_sdk_span> parent_span,
std::size_t retry_attempts);

void
init_observability(VALUE cBackend);
} // namespace couchbase::ruby
Loading
Loading