From 83a6f1289feaf2c265f8601b566374802d4c05fd Mon Sep 17 00:00:00 2001 From: Michal Maslanka Date: Thu, 1 Apr 2021 09:04:04 +0200 Subject: [PATCH 01/95] httpd: added listener index to http request Seastar http server implementation supports multiple listeners. It may be required for the handler logic to know which listener the connection is coming from. Added listener_idx field to `httpd::request` to allow handler recognize listener. Signed-off-by: Michal Maslanka --- include/seastar/http/httpd.hh | 19 +++++++++++-------- include/seastar/http/request.hh | 8 ++++++++ src/http/httpd.cc | 5 +++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/include/seastar/http/httpd.hh b/include/seastar/http/httpd.hh index 543a8085662..95bd1f7d7d0 100644 --- a/include/seastar/http/httpd.hh +++ b/include/seastar/http/httpd.hh @@ -79,29 +79,32 @@ class connection : public boost::intrusive::list_base_hook<> { queue> _replies { 10 }; bool _done = false; const bool _tls; + int _listener_idx; public: - [[deprecated("use connection(http_server&, connected_socket&&, bool tls)")]] - connection(http_server& server, connected_socket&& fd, socket_address, bool tls) - : connection(server, std::move(fd), tls) {} - connection(http_server& server, connected_socket&& fd, bool tls) + [[deprecated("use connection(http_server&, connected_socket&&, bool tls, int listener_idx)")]] + connection(http_server& server, connected_socket&& fd, socket_address, bool tls, int listener_idx) + : connection(server, std::move(fd), tls, listener_idx) {} + connection(http_server& server, connected_socket&& fd, bool tls, int listener_idx) : _server(server) , _fd(std::move(fd)) , _read_buf(_fd.input()) , _write_buf(_fd.output()) , _client_addr(_fd.remote_address()) , _server_addr(_fd.local_address()) - , _tls(tls) { + , _tls(tls) + , _listener_idx(listener_idx) { on_new_connection(); } connection(http_server& server, connected_socket&& fd, - socket_address client_addr, socket_address server_addr, bool tls) + socket_address client_addr, socket_address server_addr, bool tls, int listener_idx) : _server(server) , _fd(std::move(fd)) , _read_buf(_fd.input()) , _write_buf(_fd.output()) , _client_addr(std::move(client_addr)) - , _server_addr(std::move(server_addr)) - , _tls(tls) { + , _server_addr(std::move(server_addr)) + , _tls(tls) + , _listener_idx(listener_idx) { on_new_connection(); } ~connection(); diff --git a/include/seastar/http/request.hh b/include/seastar/http/request.hh index d27604b6a06..67d2a71768a 100644 --- a/include/seastar/http/request.hh +++ b/include/seastar/http/request.hh @@ -77,6 +77,7 @@ struct request { std::unordered_map chunk_extensions; sstring protocol_name = "http"; noncopyable_function(output_stream&&)> body_writer; // for client + int listener_idx; /** * Get the address of the client that generated the request @@ -152,6 +153,13 @@ struct request { bool is_form_post() const { return content_type_class == ctclass::app_x_www_urlencoded; } + /** + * Get index of listener which accepted connection receiving this request + * @return position of listener in server _listeners vector + */ + int get_listener_idx() const { + return listener_idx; + } bool should_keep_alive() const { if (_version == "0.9") { diff --git a/src/http/httpd.cc b/src/http/httpd.cc index ffb67ae4cb0..96ad7934c5f 100644 --- a/src/http/httpd.cc +++ b/src/http/httpd.cc @@ -225,6 +225,7 @@ future<> connection::read_one() { req->_server_address = this->_server_addr; req->_client_address = this->_client_addr; + req->listener_idx = _listener_idx; if (_tls) { req->protocol_name = "https"; @@ -448,10 +449,10 @@ future<> http_server::do_accepts(int which){ } future<> http_server::do_accept_one(int which, bool tls) { - return _listeners[which].accept().then([this, tls] (accept_result ar) mutable { + return _listeners[which].accept().then([this, tls, which] (accept_result ar) mutable { auto local_address = ar.connection.local_address(); auto conn = std::make_unique(*this, std::move(ar.connection), - std::move(ar.remote_address), std::move(local_address), tls); + std::move(ar.remote_address), std::move(local_address), tls, which); (void)try_with_gate(_task_gate, [conn = std::move(conn)]() mutable { return conn->process().handle_exception([conn = std::move(conn)] (std::exception_ptr ex) { hlogger.error("request error: {}", ex); From e667a1b09107a889c544b37505d6b89a5db28784 Mon Sep 17 00:00:00 2001 From: John Spray Date: Thu, 9 Dec 2021 14:10:25 +0000 Subject: [PATCH 02/95] http: enable specifying a content type on exceptions Since an exception carries some text for the response body text, the raising site might like to specify the content type if it's e.g. json. Signed-off-by: John Spray --- include/seastar/http/exception.hh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/seastar/http/exception.hh b/include/seastar/http/exception.hh index 5b43284c82b..4d685602059 100644 --- a/include/seastar/http/exception.hh +++ b/include/seastar/http/exception.hh @@ -43,6 +43,16 @@ public: : _msg(msg), _status(status) { } + /** + * A base_exception with a content_type is specifying a full response body, whereas + * a base_exception with only a _status is specifying a string that may be wrapped + * in e.g. a json_exception. + */ + base_exception(const std::string& msg, http::reply::status_type status, const std::string &content_type) + : _msg(msg), _status(status), _content_type(content_type) { + } + + virtual const char* what() const noexcept { return _msg.c_str(); } @@ -54,9 +64,14 @@ public: virtual const std::string& str() const { return _msg; } + + virtual const std::string& content_type() const { + return _content_type; + } private: std::string _msg; http::reply::status_type _status; + std::string _content_type; }; From ef5cda8514966a2127378065cc1404fc4d9dc6ca Mon Sep 17 00:00:00 2001 From: John Spray Date: Thu, 9 Dec 2021 14:11:07 +0000 Subject: [PATCH 03/95] http: don't jsonize exception if has content type This enables throwing a base_exception from a json request handler with a json payload inside it. Signed-off-by: John Spray --- src/http/routes.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/http/routes.cc b/src/http/routes.cc index 0b3b72a8be3..46cd6116293 100644 --- a/src/http/routes.cc +++ b/src/http/routes.cc @@ -83,7 +83,12 @@ std::unique_ptr routes::exception_reply(std::exception_ptr eptr) { rep.reset(new http::reply()); rep->add_header("Location", _e.url).set_status(_e.status()); } catch (const base_exception& e) { - rep->set_status(e.status(), internal::json_exception(e).to_json()); + if (e.content_type().size()) { + rep->set_status(e.status(), e.str()); + rep->set_content_type(e.content_type()); + } else { + rep->set_status(e.status(), internal::json_exception(e).to_json()); + } } catch (...) { rep->set_status(http::reply::status_type::internal_server_error, internal::json_exception(std::current_exception()).to_json()); From 842e408a7b892eb38aebb1638f027572cadd8dc3 Mon Sep 17 00:00:00 2001 From: John Spray Date: Thu, 9 Dec 2021 14:29:14 +0000 Subject: [PATCH 04/95] http: use base_exception content type in non-json errors Signed-off-by: John Spray --- include/seastar/http/httpd.hh | 2 +- src/http/httpd.cc | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/seastar/http/httpd.hh b/include/seastar/http/httpd.hh index 95bd1f7d7d0..70eeca92fe2 100644 --- a/include/seastar/http/httpd.hh +++ b/include/seastar/http/httpd.hh @@ -122,7 +122,7 @@ public: future<> start_response(); future generate_reply(std::unique_ptr req); - void generate_error_reply_and_close(std::unique_ptr req, http::reply::status_type status, const sstring& msg); + void generate_error_reply_and_close(std::unique_ptr req, http::reply::status_type status, const sstring& msg, const sstring &content_type={}); future<> write_body(); diff --git a/src/http/httpd.cc b/src/http/httpd.cc index 96ad7934c5f..b6daf3fc043 100644 --- a/src/http/httpd.cc +++ b/src/http/httpd.cc @@ -203,11 +203,14 @@ set_request_content(std::unique_ptr req, input_stream* cont } } -void connection::generate_error_reply_and_close(std::unique_ptr req, http::reply::status_type status, const sstring& msg) { +void connection::generate_error_reply_and_close(std::unique_ptr req, http::reply::status_type status, const sstring& msg, const sstring &content_type) { auto resp = std::make_unique(); // TODO: Handle HTTP/2.0 when it releases resp->set_version(req->_version); resp->set_status(status, msg); + if (!content_type.empty()) { + resp->set_content_type(content_type); + } resp->done(); _done = true; _replies.push(std::move(resp)); @@ -294,7 +297,7 @@ future<> connection::read_one() { // before passing the request to handler - when we were parsing chunks auto err_req = std::make_unique(); err_req->_version = version; - generate_error_reply_and_close(std::move(err_req), e.status(), e.str()); + generate_error_reply_and_close(std::move(err_req), e.status(), e.str(), e.content_type()); }); }); }); From 4fd47466c3eb3300c3450e91d9a18d224aae6964 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 1 Jun 2022 13:35:42 +0100 Subject: [PATCH 05/95] metrics: allow multiple metrics::impl instances Prior to this patch seastar only exposes one global metrics::impl::impl object which holds all metric related data for one application. This patch changes the implementation details such that multiple metrics::impl::impl objects can exist for any given application. Said objects are stored into a map on each shard and created dinamically whenever requested. A metrics::impl::impl is identified by an integer handle that acts as the key for the storage map. Implementation note: in order to avoid issues caused by the ordering of static thread_local objects I had to declare the storage in reactor.cc. (cherry picked from commit 585a8af6a586e161bbfc93a087d06c9c30175ca0) --- include/seastar/core/metrics_api.hh | 7 ++++++- src/core/metrics.cc | 19 ++++++++++++++----- src/core/reactor.cc | 8 ++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/include/seastar/core/metrics_api.hh b/include/seastar/core/metrics_api.hh index 6c9ae7fdff5..cc0d789ca2c 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -45,6 +45,9 @@ namespace impl { using labels_type = std::map; using internalized_labels_ref = lw_shared_ptr; + +int default_handle(); + } } } @@ -237,6 +240,8 @@ inline bool operator<(const internalized_holder& lhs, const internalized_holder& class impl; +using metric_implementations = std::unordered_map>; +metric_implementations& get_metric_implementations(); class registered_metric final { metric_info _info; @@ -517,7 +522,7 @@ using values_reference = shared_ptr; foreign_ptr get_values(); -shared_ptr get_local_impl(); +shared_ptr get_local_impl(int handle = default_handle()); void unregister_metric(const metric_id & id); diff --git a/src/core/metrics.cc b/src/core/metrics.cc index d6ddadeb0a5..a40c0119f39 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -369,13 +369,17 @@ bool metric_id::operator==( return as_tuple() == id2.as_tuple(); } -// Unfortunately, metrics_impl can not be shared because it -// need to be available before the first users (reactor) will call it +shared_ptr get_local_impl(int handle) { + auto& impls = get_metric_implementations(); + auto [it, inserted] = impls.try_emplace(handle); -shared_ptr get_local_impl() { - static thread_local auto the_impl = ::seastar::make_shared(); - return the_impl; + if (inserted) { + it->second = ::seastar::make_shared(); + } + + return it->second; } + void impl::remove_registration(const metric_id& id) { auto i = get_value_map().find(id.full_name()); if (i != get_value_map().end()) { @@ -582,6 +586,11 @@ void impl::set_metric_family_configs(const std::vector& fa } } } + +int default_handle() { + return 0; +} + } const bool metric_disabled = false; diff --git a/src/core/reactor.cc b/src/core/reactor.cc index 37d0629b045..96089052ff0 100644 --- a/src/core/reactor.cc +++ b/src/core/reactor.cc @@ -131,6 +131,7 @@ module seastar; #include #include #include +#include #include #include #include @@ -176,6 +177,7 @@ module seastar; #include "core/reactor_backend.hh" #include "core/syscall_result.hh" #include "core/thread_pool.hh" +#include "core/scollectd-impl.hh" #include "syscall_work_queue.hh" #include "cgroup.hh" #ifdef SEASTAR_HAVE_DPDK @@ -3930,6 +3932,12 @@ smp_options::smp_options(program_options::option_group* parent_group) { } +thread_local metrics::impl::metric_implementations metric_impls; + +metrics::impl::metric_implementations& metrics::impl::get_metric_implementations() { + return metric_impls; +} + struct reactor_deleter { void operator()(reactor* p) { p->~reactor(); From 3ac522028c891b9fb28bb5284c7ef7f2781bce63 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 22 Jun 2022 17:31:25 +0100 Subject: [PATCH 06/95] metrics: expose metric impl handle to internal api This patch extends the metrics internal apis to use a specific metrics::impl::impl object identified by its integer handle. (cherry picked from commit 6ee4af7) --- include/seastar/core/metrics_api.hh | 14 ++++--- include/seastar/core/metrics_registration.hh | 3 ++ src/core/metrics.cc | 39 +++++++++++--------- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/include/seastar/core/metrics_api.hh b/include/seastar/core/metrics_api.hh index cc0d789ca2c..e6240494f9e 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -282,10 +282,11 @@ using metric_instances = std::map using metrics_registration = std::vector; class metric_groups_impl : public metric_groups_def { + int _handle; metrics_registration _registration; shared_ptr _impl; // keep impl alive while metrics are registered public: - metric_groups_impl(); + explicit metric_groups_impl(int handle = default_handle()); ~metric_groups_impl(); metric_groups_impl(const metric_groups_impl&) = delete; metric_groups_impl(metric_groups_impl&&) = default; @@ -517,14 +518,15 @@ private: bool apply_relabeling(const relabel_config& rc, metric_info& info); }; -const value_map& get_value_map(); +const value_map& get_value_map(int handle = default_handle()); using values_reference = shared_ptr; -foreign_ptr get_values(); +foreign_ptr get_values(int handle = default_handle()); shared_ptr get_local_impl(int handle = default_handle()); -void unregister_metric(const metric_id & id); + +void unregister_metric(const metric_id & id, int handle = default_handle()); /*! * \brief initialize metric group @@ -532,7 +534,7 @@ void unregister_metric(const metric_id & id); * Create a metric_group_def. * No need to use it directly. */ -std::unique_ptr create_metric_groups(); +std::unique_ptr create_metric_groups(int handle = default_handle()); } @@ -549,7 +551,7 @@ struct options : public program_options::option_group { /*! * \brief set the metrics configuration */ -future<> configure(const options& opts); +future<> configure(const options& opts, int handle = default_handle()); /*! * \brief Perform relabeling and operation on metrics dynamically. diff --git a/include/seastar/core/metrics_registration.hh b/include/seastar/core/metrics_registration.hh index 341903089be..1908a34f9c8 100644 --- a/include/seastar/core/metrics_registration.hh +++ b/include/seastar/core/metrics_registration.hh @@ -54,6 +54,7 @@ namespace seastar { namespace metrics { namespace impl { +int default_handle(); class metric_groups_def; struct metric_definition_impl; class metric_groups_impl; @@ -61,6 +62,8 @@ class metric_groups_impl; SEASTAR_MODULE_EXPORT_BEGIN +int default_handle(); + using group_name_type = sstring; /*!< A group of logically related metrics */ class metric_groups; diff --git a/src/core/metrics.cc b/src/core/metrics.cc index a40c0119f39..a3e7fa30de3 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -46,6 +46,10 @@ namespace seastar { extern seastar::logger seastar_logger; namespace metrics { +int default_handle() { + return impl::default_handle(); +}; + double_registration::double_registration(std::string what): std::runtime_error(what) {} metric_groups::metric_groups() noexcept : _impl(impl::create_metric_groups()) { @@ -117,11 +121,11 @@ options::options(program_options::option_group* parent_group) { } -future<> configure(const options& opts) { +future<> configure(const options& opts, int handle) { impl::config c; c.hostname = opts.metrics_hostname.get_value(); - return smp::invoke_on_all([c] { - impl::get_local_impl()->set_config(c); + return smp::invoke_on_all([c, handle] { + impl::get_local_impl(handle)->set_config(c); }); } @@ -296,15 +300,15 @@ metric_definition_impl& metric_definition_impl::set_skip_when_empty(bool skip) n return *this; } -std::unique_ptr create_metric_groups() { - return std::make_unique(); +std::unique_ptr create_metric_groups(int handle) { + return std::make_unique(handle); } -metric_groups_impl::metric_groups_impl() {} +metric_groups_impl::metric_groups_impl(int handle) : _handle(handle) {} metric_groups_impl::~metric_groups_impl() { for (const auto& i : _registration) { - unregister_metric(i->info().id); + unregister_metric(i->info().id, _handle); } } @@ -322,14 +326,15 @@ metric_groups_impl& metric_groups_impl::add_metric(group_name_type name, const m // than where the actual metrics are added. // Hence, the shared_ptr owning shard check would fail so we do it only here. if (_impl == nullptr) { - _impl = get_local_impl(); + _impl = get_local_impl(_handle); } - auto internalized_labels = get_local_impl()->internalize_labels(md._impl->labels); + auto internalized_labels = get_local_impl(_handle)->internalize_labels(md._impl->labels); metric_id id(name, md._impl->name, internalized_labels); - auto reg = get_local_impl()->add_registration(id, md._impl->type, md._impl->f, md._impl->d, md._impl->enabled, md._impl->_skip_when_empty, md._impl->aggregate_labels); + auto reg = get_local_impl(_handle)->add_registration( + id, md._impl->type, md._impl->f, md._impl->d, md._impl->enabled, md._impl->_skip_when_empty, md._impl->aggregate_labels); _registration.push_back(std::move(reg)); return *this; @@ -395,20 +400,20 @@ void impl::remove_registration(const metric_id& id) { } } -void unregister_metric(const metric_id & id) { - get_local_impl()->remove_registration(id); +void unregister_metric(const metric_id & id, int handle) { + get_local_impl(handle)->remove_registration(id); } -const value_map& get_value_map() { - return get_local_impl()->get_value_map(); +const value_map& get_value_map(int handle) { + return get_local_impl(handle)->get_value_map(); } -foreign_ptr get_values() { +foreign_ptr get_values(int handle) { shared_ptr res_ref = ::seastar::make_shared(); auto& res = *(res_ref.get()); auto& mv = res.values; - res.metadata = get_local_impl()->metadata(); - auto & functions = get_local_impl()->functions(); + res.metadata = get_local_impl(handle)->metadata(); + auto & functions = get_local_impl(handle)->functions(); for (auto&& i : functions) { value_vector values; for (auto&& v : i) { From 5db62b13f1648aa3e3b75f6b6cbb0cc1acbea1e4 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Mon, 18 Jul 2022 18:58:56 +0100 Subject: [PATCH 07/95] metrics: expose handle in metric_groups_impl Add a public method to metric_groups_impl that exposes the handle of the internal implementation it is using. This is required in order for the metric_groups class to be able to reset itself to the configured implementation handle. --- include/seastar/core/metrics.hh | 1 + include/seastar/core/metrics_api.hh | 1 + src/core/metrics.cc | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/include/seastar/core/metrics.hh b/include/seastar/core/metrics.hh index 73bc293a046..d2f51f4f6bf 100644 --- a/include/seastar/core/metrics.hh +++ b/include/seastar/core/metrics.hh @@ -405,6 +405,7 @@ public: virtual metric_groups_def& add_metric(group_name_type name, const metric_definition& md) = 0; virtual metric_groups_def& add_group(group_name_type name, const std::initializer_list& l) = 0; virtual metric_groups_def& add_group(group_name_type name, const std::vector& l) = 0; + virtual int get_handle() const = 0; }; instance_id_type shard(); diff --git a/include/seastar/core/metrics_api.hh b/include/seastar/core/metrics_api.hh index e6240494f9e..033659b55b3 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -293,6 +293,7 @@ public: metric_groups_impl& add_metric(group_name_type name, const metric_definition& md); metric_groups_impl& add_group(group_name_type name, const std::initializer_list& l); metric_groups_impl& add_group(group_name_type name, const std::vector& l); + int get_handle() const; }; class metric_family { diff --git a/src/core/metrics.cc b/src/core/metrics.cc index a3e7fa30de3..2e7758a225e 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -354,6 +354,10 @@ metric_groups_impl& metric_groups_impl::add_group(group_name_type name, const st return *this; } +int metric_groups_impl::get_handle() const { + return _handle; +} + bool metric_id::operator<( const metric_id& id2) const { return as_tuple() < id2.as_tuple(); From 0154d1783f29fb379f5793f5663e18f995e9d61b Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 22 Jun 2022 17:34:52 +0100 Subject: [PATCH 08/95] metrics: expose metric impl handle to external api This patch extends the metrics user facing apis to use a specific metrics::impl::impl object identified by its integer handle. Note that the constructor of 'metric_groups' is marked explicit in this patch and updates two call sites where the constructor was used implicitly. --- include/seastar/core/metrics_registration.hh | 8 ++++---- src/core/io_queue.cc | 2 +- src/core/metrics.cc | 12 ++++++------ src/core/reactor.cc | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/seastar/core/metrics_registration.hh b/include/seastar/core/metrics_registration.hh index 1908a34f9c8..0e5e3547486 100644 --- a/include/seastar/core/metrics_registration.hh +++ b/include/seastar/core/metrics_registration.hh @@ -97,7 +97,7 @@ public: class metric_groups { std::unique_ptr _impl; public: - metric_groups() noexcept; + explicit metric_groups(int handle = default_handle()) noexcept; metric_groups(metric_groups&&) = default; virtual ~metric_groups(); metric_groups& operator=(metric_groups&&) = default; @@ -106,7 +106,7 @@ public: * * combine the constructor with the add_group functionality. */ - metric_groups(std::initializer_list mg); + metric_groups(std::initializer_list mg, int handle = default_handle()); /*! * \brief Add metrics belonging to the same group. @@ -162,7 +162,7 @@ public: */ class metric_group : public metric_groups { public: - metric_group() noexcept; + explicit metric_group(int handle = default_handle()) noexcept; metric_group(const metric_group&) = delete; metric_group(metric_group&&) = default; virtual ~metric_group(); @@ -173,7 +173,7 @@ public: * * */ - metric_group(const group_name_type& name, std::initializer_list l); + metric_group(const group_name_type& name, std::initializer_list l, int handle = default_handle()); }; SEASTAR_MODULE_EXPORT_END diff --git a/src/core/io_queue.cc b/src/core/io_queue.cc index 1b43035abed..1db44adc7d1 100644 --- a/src/core/io_queue.cc +++ b/src/core/io_queue.cc @@ -822,7 +822,7 @@ void io_queue::register_stats(sstring name, priority_class_data& pc) { } new_metrics.add_group("io_queue", std::move(metrics)); - pc.metric_groups = std::exchange(new_metrics, {}); + pc.metric_groups = std::exchange(new_metrics, sm::metric_groups{}); } io_queue::priority_class_data& io_queue::find_or_create_class(internal::priority_class pc) { diff --git a/src/core/metrics.cc b/src/core/metrics.cc index 2e7758a225e..e8e902c7a85 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -52,14 +52,15 @@ int default_handle() { double_registration::double_registration(std::string what): std::runtime_error(what) {} -metric_groups::metric_groups() noexcept : _impl(impl::create_metric_groups()) { +metric_groups::metric_groups(int handle) noexcept : _impl(impl::create_metric_groups(handle)) { } void metric_groups::clear() { - _impl = impl::create_metric_groups(); + const auto current_handle = _impl->get_handle(); + _impl = impl::create_metric_groups(current_handle); } -metric_groups::metric_groups(std::initializer_list mg) : _impl(impl::create_metric_groups()) { +metric_groups::metric_groups(std::initializer_list mg, int handle) : _impl(impl::create_metric_groups(handle)) { for (auto&& i : mg) { add_group(i.name, i.metrics); } @@ -72,10 +73,9 @@ metric_groups& metric_groups::add_group(const group_name_type& name, const std:: _impl->add_group(name, l); return *this; } -metric_group::metric_group() noexcept = default; +metric_group::metric_group(int handle) noexcept : metric_groups(handle) {} metric_group::~metric_group() = default; -metric_group::metric_group(const group_name_type& name, std::initializer_list l) { - add_group(name, l); +metric_group::metric_group(const group_name_type& name, std::initializer_list l, int handle) : metric_groups({metric_group_definition(name, l)}, handle) { } metric_group_definition::metric_group_definition(const group_name_type& name, std::initializer_list l) : name(name), metrics(l) { diff --git a/src/core/reactor.cc b/src/core/reactor.cc index 96089052ff0..46f2c69caa9 100644 --- a/src/core/reactor.cc +++ b/src/core/reactor.cc @@ -970,7 +970,7 @@ reactor::task_queue::register_stats() { register_net_metrics_for_scheduling_group(new_metrics, _id, group_label); - _metrics = std::exchange(new_metrics, {}); + _metrics = std::exchange(new_metrics, sm::metric_groups{}); } void From c808b5bca3b794b84e7fa31d4cc87cfb4acd0ab7 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 22 Jun 2022 14:06:39 +0100 Subject: [PATCH 09/95] metrics: Use handle for impl object This patch removes two subsequent calls to `get_local_impl` and reuses the returned handle in that scope. --- src/core/metrics.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/metrics.cc b/src/core/metrics.cc index e8e902c7a85..1f8e830c16e 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -416,8 +416,9 @@ foreign_ptr get_values(int handle) { shared_ptr res_ref = ::seastar::make_shared(); auto& res = *(res_ref.get()); auto& mv = res.values; - res.metadata = get_local_impl(handle)->metadata(); - auto & functions = get_local_impl(handle)->functions(); + auto impl = get_local_impl(handle); + res.metadata = impl->metadata(); + auto & functions = impl->functions(); for (auto&& i : functions) { value_vector values; for (auto&& v : i) { From b11f0e66de190b652be6842b241a00331ae5bfea Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 1 Jun 2022 13:42:25 +0100 Subject: [PATCH 10/95] prometheus: support multiple metric impls This patch extends the user facing prometheus apis allowing the user to specify the internal metrics implementation to be used through a handle. Additionally, 'add_prometheus_routes' now takes an argument that specifies the route on which to advertise the metrics. This enables different metrics "namespaces" to be served by different endpoints in isolation. (cherry picked from commit 6189522fd8247894fae1bd664a7305484cd22724) --- include/seastar/core/prometheus.hh | 7 +++++-- src/core/prometheus.cc | 12 ++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/include/seastar/core/prometheus.hh b/include/seastar/core/prometheus.hh index 25058280515..db6686912f5 100644 --- a/include/seastar/core/prometheus.hh +++ b/include/seastar/core/prometheus.hh @@ -44,12 +44,15 @@ struct config { std::optional label; //!< A label that will be added to all metrics, we advice not to use it and set it on the prometheus server sstring prefix = "seastar"; //!< a prefix that will be added to metric names bool allow_protobuf = false; // protobuf support is experimental and off by default + int handle = metrics::default_handle(); //!< Handle that specifies which metric implementation to query + sstring route = "/metrics"; //!< Name of the route on which to expose the metrics }; future<> start(httpd::http_server_control& http_server, config ctx); -/// \defgroup add_prometheus_routes adds a /metrics endpoint that returns prometheus metrics -/// both in txt format and in protobuf according to the prometheus spec +/// \defgroup add_prometheus_routes adds a specified endpoint (defaults to /metrics) that returns prometheus metrics +/// in txt format format and in protobuf according to the prometheus spec + /// @{ future<> add_prometheus_routes(distributed& server, config ctx); future<> add_prometheus_routes(httpd::http_server& server, config ctx); diff --git a/src/core/prometheus.cc b/src/core/prometheus.cc index 53bf093f593..d4c3eaaac0c 100644 --- a/src/core/prometheus.cc +++ b/src/core/prometheus.cc @@ -401,11 +401,11 @@ class metrics_families_per_shard { /** @} */ }; -static future<> get_map_value(metrics_families_per_shard& vec) { +static future<> get_map_value(metrics_families_per_shard& vec, int handle) { vec.resize(smp::count); - return parallel_for_each(std::views::iota(0u, smp::count), [&vec] (auto cpu) { - return smp::submit_to(cpu, [] { - return mi::get_values(); + return parallel_for_each(std::views::iota(0u, smp::count), [handle, &vec] (auto cpu) { + return smp::submit_to(cpu, [handle] { + return mi::get_values(handle); }).then([&vec, cpu] (auto res) { vec[cpu] = std::move(res); }); @@ -930,7 +930,7 @@ class metrics_handler : public httpd::handler_base { rep->write_body(is_protobuf_format ? "proto" : "txt", [this, is_protobuf_format, metric_family_name, prefix, show_help, enable_aggregation, filter] (output_stream&& s) { return do_with(metrics_families_per_shard(), output_stream(std::move(s)), [this, is_protobuf_format, prefix, &metric_family_name, show_help, enable_aggregation, filter] (metrics_families_per_shard& families, output_stream& s) mutable { - return get_map_value(families).then([&s, &families, this, is_protobuf_format, prefix, &metric_family_name, show_help, enable_aggregation, filter]() mutable { + return get_map_value(families, _ctx.handle).then([&s, &families, this, is_protobuf_format, prefix, &metric_family_name, show_help, enable_aggregation, filter]() mutable { return do_with(get_range(families, metric_family_name, prefix), [&s, this, is_protobuf_format, show_help, enable_aggregation, filter](metric_family_range& m) { return (is_protobuf_format) ? write_protobuf_representation(s, _ctx, m, enable_aggregation, filter) : @@ -950,7 +950,7 @@ std::function metrics_handler::_true_function = [] }; future<> add_prometheus_routes(httpd::http_server& server, config ctx) { - server._routes.put(httpd::GET, "/metrics", new metrics_handler(ctx)); + server._routes.put(httpd::GET, ctx.route, new metrics_handler(ctx)); return make_ready_future<>(); } From d793184cc3c51f00dda49b0cf6930df6c4c6c0eb Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 1 Jun 2022 13:42:57 +0100 Subject: [PATCH 11/95] scollectd: select internal metrics implementation This patch extends the scollectd apis with the ability to select the internal metrics implementation to be used by providing a handle. (cherry picked from commit d4331d148af54213890cf1d2fe05e8df05cdd504) --- include/seastar/core/scollectd.hh | 10 +++++----- src/core/scollectd.cc | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/seastar/core/scollectd.hh b/include/seastar/core/scollectd.hh index 83ff82aacea..9c9191cd081 100644 --- a/include/seastar/core/scollectd.hh +++ b/include/seastar/core/scollectd.hh @@ -374,7 +374,7 @@ struct options : public program_options::option_group { /// \endcond }; -void configure(const options&); +void configure(const options&, int handle = seastar::metrics::default_handle()); void remove_polled_metric(const type_instance_id &); class plugin_instance_metrics; @@ -393,8 +393,8 @@ class plugin_instance_metrics; */ struct registration { registration() = default; - registration(const type_instance_id& id); - registration(type_instance_id&& id); + registration(const type_instance_id& id, int handle = seastar::metrics::default_handle()); + registration(type_instance_id&& id, int handle = seastar::metrics::default_handle()); registration(const registration&) = delete; registration(registration&&) = default; ~registration(); @@ -782,8 +782,8 @@ seastar::metrics::impl::metric_id to_metrics_id(const type_instance_id & id); */ template [[deprecated("Use the metrics layer")]] static type_instance_id add_polled_metric(const type_instance_id & id, description d, - Arg&& arg, bool enabled = true) { - seastar::metrics::impl::get_local_impl()->add_registration(to_metrics_id(id), arg.type, seastar::metrics::impl::make_function(arg.value, arg.type), d, enabled); + Arg&& arg, bool enabled = true, int handle = seastar::metrics::default_handle()) { + seastar::metrics::impl::get_local_impl(handle)->add_registration(to_metrics_id(id), arg.type, seastar::metrics::impl::make_function(arg.value, arg.type), d, enabled); return id; } /*! diff --git a/src/core/scollectd.cc b/src/core/scollectd.cc index a6075ddc3d9..cc6482a56d8 100644 --- a/src/core/scollectd.cc +++ b/src/core/scollectd.cc @@ -89,12 +89,12 @@ registration::~registration() { unregister(); } -registration::registration(const type_instance_id& id) -: _id(id), _impl(seastar::metrics::impl::get_local_impl()) { +registration::registration(const type_instance_id& id, int handle) +: _id(id), _impl(seastar::metrics::impl::get_local_impl(handle)) { } -registration::registration(type_instance_id&& id) -: _id(std::move(id)), _impl(seastar::metrics::impl::get_local_impl()) { +registration::registration(type_instance_id&& id, int handle) +: _id(std::move(id)), _impl(seastar::metrics::impl::get_local_impl(handle)) { } seastar::metrics::impl::metric_id to_metrics_id(const type_instance_id & id) { @@ -544,7 +544,7 @@ future<> send_metric(const type_instance_id & id, return get_impl().send_metric(id, values); } -void configure(const options& opts) { +void configure(const options& opts, int handle) { bool enable = opts.collectd.get_value(); if (!enable) { return; @@ -553,7 +553,7 @@ void configure(const options& opts) { auto period = std::chrono::milliseconds(opts.collectd_poll_period.get_value()); auto host = (opts.collectd_hostname.get_value() == "") - ? seastar::metrics::impl::get_local_impl()->get_config().hostname + ? seastar::metrics::impl::get_local_impl(handle)->get_config().hostname : sstring(opts.collectd_hostname.get_value()); // Now create send loops on each cpu From 74842fa062d5c04e76538e5fdf1b4b6cd8cf7a8b Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Thu, 7 Jul 2022 11:52:21 +0100 Subject: [PATCH 12/95] metrics: Expose 'skip_when_empty' for metrics This patch adds a 'get_skip_when_empy' getter to the 'registered_metric' class. It is used by follow-up patches in order to replicate metrics. --- include/seastar/core/metrics_api.hh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/seastar/core/metrics_api.hh b/include/seastar/core/metrics_api.hh index 033659b55b3..95b42250b86 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -262,6 +262,11 @@ public: void set_skip_when_empty(skip_when_empty skip) noexcept { _info.should_skip_when_empty = skip; } + + skip_when_empty get_skip_when_empty() const { + return _info.should_skip_when_empty; + } + const metric_id& get_id() const { return _info.id; } From 6bf71b3178d7fd5eb09c7f929ee03001dc3e1586 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Tue, 12 Jul 2022 12:24:50 +0100 Subject: [PATCH 13/95] metrics: add helpers for creation of replicas This patch adds private methods to the 'metrics::impl' class that deal with the creation of replicated metrics. They will be used to build the public api in future commits. --- include/seastar/core/metrics_api.hh | 9 ++++++ src/core/metrics.cc | 45 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/include/seastar/core/metrics_api.hh b/include/seastar/core/metrics_api.hh index 95b42250b86..93f13fdb310 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -470,6 +470,7 @@ class impl { std::vector _relabel_configs; std::vector _metric_family_configs; internalized_set _internalized_labels; + std::unordered_multimap _metric_families_to_replicate; public: value_map& get_value_map() { return _value_map; @@ -511,6 +512,7 @@ public: const std::vector& get_relabel_configs() const noexcept { return _relabel_configs; } + const std::vector& get_metric_family_configs() const noexcept { return _metric_family_configs; } @@ -522,6 +524,13 @@ public: private: void gc_internalized_labels(); bool apply_relabeling(const relabel_config& rc, metric_info& info); + void replicate_metric_family(const seastar::sstring& name, + int destination_handle) const; + void replicate_metric_if_required(const shared_ptr& metric) const; + void replicate_metric(const shared_ptr& metric, + const metric_family& family, + const shared_ptr& destination, + int destination_handle) const; }; const value_map& get_value_map(int handle = default_handle()); diff --git a/src/core/metrics.cc b/src/core/metrics.cc index 1f8e830c16e..ce9ca9476b6 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -444,6 +444,51 @@ void impl::gc_internalized_labels() { } } +void impl::replicate_metric_family(const seastar::sstring& name, + int destination_handle) const { + const auto& entry = _value_map.find(name); + + if (entry == _value_map.end()) { + return; + } + + const auto& metric_family = entry->second; + auto destination = get_local_impl(destination_handle); + for (const auto& [labels, metric_ptr]: metric_family) { + replicate_metric(metric_ptr, metric_family, destination, destination_handle); + } +} + +void impl::replicate_metric_if_required(const shared_ptr& metric) const { + auto full_name = metric->get_id().full_name(); + auto [begin, end]= _metric_families_to_replicate.equal_range(full_name); + + for (; begin != end; ++begin) { + const auto& [name, destination_handle] = *begin; + const auto& metric_family = _value_map.at(name); + + auto destination = get_local_impl(destination_handle); + replicate_metric(metric, metric_family, destination, destination_handle); + } +} + +void impl::replicate_metric(const shared_ptr& metric, + const metric_family& family, + const shared_ptr& destination, + int destination_handle) const { + const auto& family_info = family.info(); + metric_type type = { .base_type = family_info.type, + .type_name = family_info.inherit_type }; + + destination->add_registration(metric->get_id(), + type, + metric->get_function(), + family_info.d, + metric->is_enabled(), + metric->get_skip_when_empty(), + family_info.aggregate_labels); +} + void impl::update_metrics_if_needed() { if (_dirty) { // Forcing the metadata to an empty initialization From df91e5b5e08eb4dfbf8e38e468fc84c32d7546f3 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Mon, 11 Jul 2022 12:43:58 +0100 Subject: [PATCH 14/95] metrics: allow for removal of replicated metrics This patch adds private helpers to 'metrics::impl' that deal with the removal of replicated metric families from their destintation implementation. These methods will be used in subsequent commits to manage the lifetime of replicated metrics. --- include/seastar/core/metrics_api.hh | 6 ++++++ src/core/metrics.cc | 30 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/include/seastar/core/metrics_api.hh b/include/seastar/core/metrics_api.hh index 93f13fdb310..f6e61cf308f 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -531,6 +531,12 @@ private: const metric_family& family, const shared_ptr& destination, int destination_handle) const; + + void remove_metric_replica_family(const seastar::sstring& name, + int destination_handle) const; + void remove_metric_replica(const metric_id& id, + const shared_ptr& destination) const; + void remove_metric_replica_if_required(const metric_id& id) const; }; const value_map& get_value_map(int handle = default_handle()); diff --git a/src/core/metrics.cc b/src/core/metrics.cc index ce9ca9476b6..b3e5fd2833b 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -404,6 +404,36 @@ void impl::remove_registration(const metric_id& id) { } } +void impl::remove_metric_replica_family(const seastar::sstring& name, + int destination_handle) const { + auto entry = _value_map.find(name); + + if (entry == _value_map.end()) { + return; + } + + auto destination = get_local_impl(destination_handle); + for (const auto& metric_instance: entry->second) { + const auto& registered_metric = metric_instance.second; + remove_metric_replica(registered_metric->get_id(), + destination); + } +} + +void impl::remove_metric_replica(const metric_id& id, + const shared_ptr& destination) const { + destination->remove_registration(id); +} + +void impl::remove_metric_replica_if_required(const metric_id& id) const { + auto [begin, end] = _metric_families_to_replicate.equal_range(id.full_name()); + + for (; begin != end; ++begin) { + auto destination = get_local_impl(begin->second); + remove_metric_replica(id, destination); + } +} + void unregister_metric(const metric_id & id, int handle) { get_local_impl(handle)->remove_registration(id); } From 2b21b5efb56dad6e04c89fed9445f883a3fede28 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Tue, 12 Jul 2022 12:27:41 +0100 Subject: [PATCH 15/95] metrics: add metric replication internal interface This patch adds a public method to the 'metrics::impl' class: 'set_metric_families_to_replicate'. When this method is called the families that match any of the specifications will be replicated on the specified destinations. --- include/seastar/core/metrics_api.hh | 16 ++++++++++++++++ src/core/metrics.cc | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/include/seastar/core/metrics_api.hh b/include/seastar/core/metrics_api.hh index f6e61cf308f..28f0003ac75 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -521,6 +521,22 @@ public: void update_aggregate(metric_family_info& mf) const noexcept; + // Set the metrics families to be replicated from this metrics::impl. + // All metrics families that match one of the keys of + // the 'metric_families_to_replicate' argument will be replicated + // on the metrics::impl identified by the corresponding value. + // + // If this function was called previously, any previously + // replicated metrics will be removed before the provided ones are + // replicated. + // + // Metric replication spans the full life cycle of this class. + // Newly registered metrics that belong to a replicated family + // be replicated too and unregistering a replicated metric will + // unregister the replica. + void set_metric_families_to_replicate( + std::unordered_multimap metric_families_to_replicate); + private: void gc_internalized_labels(); bool apply_relabeling(const relabel_config& rc, metric_info& info); diff --git a/src/core/metrics.cc b/src/core/metrics.cc index b3e5fd2833b..167b070b7c6 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -474,6 +474,22 @@ void impl::gc_internalized_labels() { } } +void +impl::set_metric_families_to_replicate( + std::unordered_multimap metric_families_to_replicate) { + // Remove all previous metric replica families + for (const auto& [name, destination]: _metric_families_to_replicate) { + remove_metric_replica_family(name, destination); + } + + // Replicate the specified metric families. + for (const auto& [name, destination]: metric_families_to_replicate) { + replicate_metric_family(name, destination); + } + + _metric_families_to_replicate = std::move(metric_families_to_replicate); +} + void impl::replicate_metric_family(const seastar::sstring& name, int destination_handle) const { const auto& entry = _value_map.find(name); From de4f99c360030946f2d87d3a84d5ddd666697483 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Mon, 11 Jul 2022 12:44:22 +0100 Subject: [PATCH 16/95] metrics: register replicated metrics dinamically This patch extends the metric registration and unregistration processes to make them aware of metric replication. In the case of metric registration, if the new metric belongs to a family that matches one of the replication specs, then a replicated metric is created accordingly. For unregistration of a metric, the replicated metric is unregistered too if one exists. --- src/core/metrics.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/metrics.cc b/src/core/metrics.cc index 167b070b7c6..0ccc8b06fa1 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -390,6 +390,8 @@ shared_ptr get_local_impl(int handle) { } void impl::remove_registration(const metric_id& id) { + remove_metric_replica_if_required(id); + auto i = get_value_map().find(id.full_name()); if (i != get_value_map().end()) { auto j = i->second.find(id.labels()); @@ -611,6 +613,8 @@ register_ref impl::add_registration(const metric_id& id, const metric_type& type } dirty(); + replicate_metric_if_required(rm); + return rm; } From a66876a8b6607326d2bf618e15f05dcc3716ab6e Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Tue, 12 Jul 2022 12:41:42 +0100 Subject: [PATCH 17/95] metrics: public family replication interface This patch exposes a method in the public interface of the metrics module ('replicate_metric_families'), which enables metric replication internally for the requested metric families. --- include/seastar/core/metrics_api.hh | 7 +++++++ src/core/metrics.cc | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/seastar/core/metrics_api.hh b/include/seastar/core/metrics_api.hh index 28f0003ac75..43eb86f8152 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -666,5 +666,12 @@ void set_metric_family_configs(const std::vector& metrics_ * This function returns a vector of the current metrics family config */ const std::vector& get_metric_family_configs(); + +/*! + * \brief replicate metric families accross internal metrics implementations + */ +future<> +replicate_metric_families(int source_handle, std::unordered_multimap metric_families_to_replicate); + } } diff --git a/src/core/metrics.cc b/src/core/metrics.cc index 0ccc8b06fa1..edf400c04d2 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -208,6 +208,17 @@ bool impl::impl::apply_relabeling(const relabel_config& rc, metric_info& info) { return true; } +future<> +replicate_metric_families( + int source_handle, + std::unordered_multimap metric_families_to_replicate) { + return smp::invoke_on_all([source_handle, metric_families_to_replicate] { + auto source_impl = impl::get_local_impl(source_handle); + source_impl->set_metric_families_to_replicate( + std::move(metric_families_to_replicate)); + }); +} + bool label_instance::operator!=(const label_instance& id2) const { auto& id1 = *this; return !(id1 == id2); From 3373d1e965adcb51be22a8a02d19dbc452934137 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Mon, 11 Jul 2022 12:49:33 +0100 Subject: [PATCH 18/95] tests: add metrics replication unit tests --- tests/unit/CMakeLists.txt | 3 + tests/unit/metric_family_replication_test.cc | 96 ++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 tests/unit/metric_family_replication_test.cc diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 2ecd66ec28f..97bdbc707c5 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -370,6 +370,9 @@ seastar_add_test (lowres_clock seastar_add_test (metrics SOURCES metrics_test.cc) +seastar_add_test (metrics_family_replication + SOURCES metric_family_replication_test.cc) + seastar_add_test (net_config KIND BOOST SOURCES net_config_test.cc) diff --git a/tests/unit/metric_family_replication_test.cc b/tests/unit/metric_family_replication_test.cc new file mode 100644 index 00000000000..2e105c1ae82 --- /dev/null +++ b/tests/unit/metric_family_replication_test.cc @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include + +namespace sm = seastar::metrics; +namespace smi = seastar::metrics::impl; + +bool metric_family_exists(int handle, const seastar::sstring& name) { + return smi::get_value_map(handle).contains(name); +} + +void assert_metric_families_equivalent(int source, int destination, + const seastar::sstring& name) { + const auto& source_value_map = smi::get_value_map(source); + const auto& destination_value_map = smi::get_value_map(destination); + + BOOST_REQUIRE(source_value_map.contains(name)); + BOOST_REQUIRE(destination_value_map.contains(name)); + + const auto& source_family = source_value_map.at(name); + const auto& destination_family = destination_value_map.at(name); + for (const auto& [labels, source_metric]: source_family) { + auto replica_iter = destination_family.find(labels.labels()); + BOOST_REQUIRE(replica_iter != destination_family.end()); + + const auto& replica_metric = replica_iter->second; + BOOST_REQUIRE(source_metric->get_id() == replica_metric->get_id()); + + auto source_current_value = source_metric->get_function()().i(); + auto replica_current_value = replica_metric->get_function()().i(); + BOOST_REQUIRE(source_current_value == replica_current_value); + } +} + +SEASTAR_THREAD_TEST_CASE(replicate_metrics_test) { + int foo_handle = sm::default_handle(); + sm::metric_groups foo(foo_handle); + foo.add_group("a", { + sm::make_gauge( + "gauge", + [] { return 0; })}); + + int bar_handle = sm::default_handle() + 1; + int baz_handle = sm::default_handle() + 2; + sm::replicate_metric_families(foo_handle, { + {"a_gauge", bar_handle}, + {"a_gauge", baz_handle} + }).get(); + + assert_metric_families_equivalent(foo_handle, bar_handle, "a_gauge"); + assert_metric_families_equivalent(foo_handle, baz_handle, "a_gauge"); +} + +SEASTAR_THREAD_TEST_CASE(replicate_same_metric_test) { + int foo_handle = sm::default_handle(); + + sm::metric_groups foo(foo_handle); + foo.add_group("a", { + sm::make_gauge( + "x", + [] { return 0; }, + sm::description("a_x_description"), + {sm::label("id")("1")}), + sm::make_gauge( + "x", + [] { return 0; }, + sm::description("a_x_description"), + {sm::label("id")("2")}), + sm::make_gauge( + "y", + [] { return 0; }, + sm::description("a_y_description"), + {sm::label("id")("1")}), + }); + + int bar_handle = sm::default_handle() + 1; + sm::metric_groups bar(bar_handle); + + // Test that subsequent attempts to replicate the same metric + // family are ignored. + sm::replicate_metric_families(foo_handle, {{"a_x", bar_handle}}).get(); + assert_metric_families_equivalent(foo_handle, bar_handle, "a_x"); + sm::replicate_metric_families(foo_handle, {{"a_x", bar_handle}}).get(); + assert_metric_families_equivalent(foo_handle, bar_handle, "a_x"); + + + // Ensure that when the set of replicated metric families is changed + // the replicas that are not in the new set are removed. + sm::replicate_metric_families(foo_handle, {{"a_y", bar_handle}}).get(); + assert_metric_families_equivalent(foo_handle, bar_handle, "a_y"); + + BOOST_REQUIRE(metric_family_exists(bar_handle, "a_y")); +} From e9a912b6b98e99ec042e5e22d7f38357bede2ff0 Mon Sep 17 00:00:00 2001 From: Stephan Dollberg Date: Thu, 19 Oct 2023 10:30:21 +0100 Subject: [PATCH 19/95] metrics: Add update_aggregate_labels() Extends the metrics api to allow changing the aggregation labels of a metrics family. Otherwise one had to un-register every single metric instance in a metric family and then re-register with the changed aggregation labels. For metric families with thousands of instances (e.g.: histograms with lots of different labels) this is quite expensive. With this change we avoid the full reconstruction of the metrics family and all its metrics. Only the work associated with marking the metrics `dirty()` is needed then. --- include/seastar/core/metrics.hh | 7 +++++++ include/seastar/core/metrics_api.hh | 1 + include/seastar/core/metrics_registration.hh | 1 + src/core/metrics.cc | 19 +++++++++++++++++++ 4 files changed, 28 insertions(+) diff --git a/include/seastar/core/metrics.hh b/include/seastar/core/metrics.hh index d2f51f4f6bf..ebc8cee3a91 100644 --- a/include/seastar/core/metrics.hh +++ b/include/seastar/core/metrics.hh @@ -680,6 +680,13 @@ impl::metric_definition_impl make_total_operations(metric_name_type name, return make_counter(name, std::forward(val), d, labels).set_type("total_operations"); } +/*! + * \brief Update the aggregation labels of a metric family + */ +void update_aggregate_labels(const group_name_type& group_name, + const metric_name_type& metric_name, + const std::vector