From c19dab9a88dc666fe8dadf0f976470cb76204759 Mon Sep 17 00:00:00 2001 From: Dimitris Christodoulou Date: Thu, 9 Oct 2025 15:43:59 +0100 Subject: [PATCH] RCBC-503: Add app telemetry options to cluster options --- ext/couchbase | 2 +- ext/rcb_backend.cxx | 37 +++++++++++++++++++++++ ext/rcb_utils.cxx | 19 ++++++++++++ ext/rcb_utils.hxx | 3 ++ lib/couchbase/options.rb | 63 ++++++++++++++++++++++++++++++++++++++-- 5 files changed, 121 insertions(+), 3 deletions(-) diff --git a/ext/couchbase b/ext/couchbase index c2439a40..ad4836b5 160000 --- a/ext/couchbase +++ b/ext/couchbase @@ -1 +1 @@ -Subproject commit c2439a404fa6b276862fe81150b1326bf98e6164 +Subproject commit ad4836b55aedb1d475d5e87514f27f630d00913e diff --git a/ext/rcb_backend.cxx b/ext/rcb_backend.cxx index 6ae097e7..87884940 100644 --- a/ext/rcb_backend.cxx +++ b/ext/rcb_backend.cxx @@ -472,6 +472,43 @@ initialize_cluster_options(const core::utils::connection_string& connstr, cluster_options.metrics().emit_interval(param.value()); } + static const auto sym_app_telemetry = rb_id2sym(rb_intern("application_telemetry")); + if (auto app_telemetry_options = options::get_hash(options, sym_app_telemetry); + app_telemetry_options) { + static const auto sym_enable_app_telemetry = rb_id2sym(rb_intern("enable")); + if (auto param = options::get_bool(app_telemetry_options.value(), sym_enable_app_telemetry); + param) { + cluster_options.application_telemetry().enable(param.value()); + } + + static const auto sym_app_telemetry_endpoint = rb_id2sym(rb_intern("override_endpoint")); + if (auto param = options::get_string(app_telemetry_options.value(), sym_app_telemetry_endpoint); + param) { + cluster_options.application_telemetry().override_endpoint(param.value()); + } + + static const auto sym_app_telemetry_backoff = rb_id2sym(rb_intern("backoff")); + if (auto param = + options::get_milliseconds(app_telemetry_options.value(), sym_app_telemetry_backoff); + param) { + cluster_options.application_telemetry().backoff_interval(param.value()); + } + + static const auto sym_app_telemetry_ping_interval = rb_id2sym(rb_intern("ping_interval")); + if (auto param = + options::get_milliseconds(app_telemetry_options.value(), sym_app_telemetry_ping_interval); + param) { + cluster_options.application_telemetry().ping_interval(param.value()); + } + + static const auto sym_app_telemetry_ping_timeout = rb_id2sym(rb_intern("ping_timeout")); + if (auto param = + options::get_milliseconds(app_telemetry_options.value(), sym_app_telemetry_ping_timeout); + param) { + cluster_options.application_telemetry().ping_timeout(param.value()); + } + } + return cluster_options; } diff --git a/ext/rcb_utils.cxx b/ext/rcb_utils.cxx index bfc513a2..c3aefc5a 100644 --- a/ext/rcb_utils.cxx +++ b/ext/rcb_utils.cxx @@ -568,6 +568,25 @@ get_symbol(VALUE options, VALUE name) return {}; } +std::optional +get_hash(VALUE options, VALUE name) +{ + if (!NIL_P(options) && TYPE(options) == T_HASH) { + cb_check_type(name, T_SYMBOL); + VALUE val = rb_hash_aref(options, name); + if (NIL_P(val)) { + return {}; + } + if (TYPE(val) == T_HASH) { + return val; + } + throw couchbase::ruby::ruby_exception( + rb_eArgError, + rb_sprintf("%+" PRIsVALUE " must be a Hash, but given %+" PRIsVALUE, name, val)); + } + return {}; +} + std::optional get_string(VALUE options, VALUE name) { diff --git a/ext/rcb_utils.hxx b/ext/rcb_utils.hxx index 9ea7b571..a74474e8 100644 --- a/ext/rcb_utils.hxx +++ b/ext/rcb_utils.hxx @@ -106,6 +106,9 @@ get_uint16_t(VALUE options, VALUE name); std::optional get_symbol(VALUE options, VALUE name); +std::optional +get_hash(VALUE options, VALUE name); + std::optional get_string(VALUE options, VALUE name); } // namespace options diff --git a/lib/couchbase/options.rb b/lib/couchbase/options.rb index 8fd09320..b9eca2a6 100644 --- a/lib/couchbase/options.rb +++ b/lib/couchbase/options.rb @@ -1706,6 +1706,10 @@ class Cluster attr_accessor :config_idle_redial_timeout # @return [nil, Integer, #in_milliseconds] attr_accessor :idle_http_connection_timeout # @return [nil, Integer, #in_milliseconds] + # @return [ApplicationTelemetry] + # @!macro volatile + attr_accessor :application_telemetry + # Creates an instance of options for {Couchbase::Cluster.connect} # # @param [PasswordAuthenticator, CertificateAuthenticator] authenticator @@ -1720,7 +1724,7 @@ class Cluster # @see .Cluster # # @yieldparam [Cluster] self - def initialize(authenticator: nil, + def initialize(authenticator: nil, # rubocop:disable Metrics/ParameterLists preferred_server_group: nil, enable_metrics: nil, metrics_emit_interval: nil, @@ -1749,7 +1753,8 @@ def initialize(authenticator: nil, config_poll_interval: nil, config_poll_floor: nil, config_idle_redial_timeout: nil, - idle_http_connection_timeout: nil) + idle_http_connection_timeout: nil, + application_telemetry: ApplicationTelemetry.new) @authenticator = authenticator @preferred_server_group = preferred_server_group @enable_metrics = enable_metrics @@ -1780,6 +1785,8 @@ def initialize(authenticator: nil, @config_poll_floor = config_poll_floor @config_idle_redial_timeout = config_idle_redial_timeout @idle_http_connection_timeout = idle_http_connection_timeout + @application_telemetry = application_telemetry + yield self if block_given? end @@ -1826,8 +1833,60 @@ def to_backend config_poll_floor: Utils::Time.extract_duration(@config_poll_floor), config_idle_redial_timeout: Utils::Time.extract_duration(@config_idle_redial_timeout), idle_http_connection_timeout: Utils::Time.extract_duration(@idle_http_connection_timeout), + application_telemetry: @application_telemetry.to_backend, } end + + # Application Telemetry Options for {Couchbase::Cluster.connect}. Part of {Couchbase::Options::Cluster} + # + # @see Options::Cluster#application_telemetry + # + # @!macro volatile + class ApplicationTelemetry + attr_accessor :enable # @return [nil, Boolean] + attr_accessor :override_endpoint # @return [nil, String] + attr_accessor :backoff # @return [nil, Integer, #in_milliseconds] + attr_accessor :ping_interval # @return [nil, Integer, #in_milliseconds] + attr_accessor :ping_timeout # @return [nil, Integer, #in_milliseconds] + + # Creates an instance of app telemetry options for {Couchbase::Cluster.connect}. + # Part of {Couchbase::Options::Cluster}. + # + # @param [nil, Boolean] enable whether to enable application telemetry capture. + # Application telemetry is enabled by default. + # @param [nil, String] override_endpoint overrides the default endpoint used for application service telemetry + # The endpoint must use the WebSocket protocol and the string should start with `ws://`. + # @param [nil, Integer, #in_milliseconds] backoff specifies the duration to wait between connection attempts + # to an application telemetry endpoint + # @param [nil, Integer, #in_milliseconds] ping_interval specifies how often the SDK should ping the application + # service telemetry collector + # @param [nil, Integer, #in_milliseconds] ping_timeout specifies how long the SDK should wait for a ping + # response (pong) from the application service collector, before closing the connection and attempting to reconnect + def initialize(enable: nil, + override_endpoint: nil, + backoff: nil, + ping_interval: nil, + ping_timeout: nil) + @enable = enable + @override_endpoint = override_endpoint + @backoff = backoff + @ping_interval = ping_interval + @ping_timeout = ping_timeout + + yield self if block_given? + end + + # @api private + def to_backend + { + enable: @enable, + override_endpoint: @override_endpoint, + backoff: Utils::Time.extract_duration(@backoff), + ping_interval: Utils::Time.extract_duration(@ping_interval), + ping_timeout: Utils::Time.extract_duration(@ping_timeout), + } + end + end end # Options for {Couchbase::Cluster#diagnostics}