From f25594bbfe8dcfe37382ee71aac46c3bb92e6d2b Mon Sep 17 00:00:00 2001 From: Michal Maslanka Date: Thu, 1 Apr 2021 09:04:04 +0200 Subject: [PATCH 01/73] 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 | 17 ++++++++++------- include/seastar/http/request.hh | 8 ++++++++ src/http/httpd.cc | 3 ++- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/include/seastar/http/httpd.hh b/include/seastar/http/httpd.hh index 181392dd4f1..31376b27d51 100644 --- a/include/seastar/http/httpd.hh +++ b/include/seastar/http/httpd.hh @@ -72,29 +72,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) { + , _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 50791fb72c4..99c7efc5328 100644 --- a/include/seastar/http/request.hh +++ b/include/seastar/http/request.hh @@ -81,6 +81,7 @@ struct request { std::unordered_map chunk_extensions; sstring protocol_name = "http"; http::body_writer_type body_writer; // for client + int listener_idx; using query_parameters_type = std::unordered_map, seastar::internal::string_view_hash, std::equal_to<>>; private: @@ -243,6 +244,13 @@ public: 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 1784e9960af..f4b5f848a3b 100644 --- a/src/http/httpd.cc +++ b/src/http/httpd.cc @@ -210,6 +210,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"; @@ -478,7 +479,7 @@ future<> http_server::do_accept_one(int which, bool tls) { } 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 c39663b7e626d310608193b3b8ec8eb7dfb3622f Mon Sep 17 00:00:00 2001 From: John Spray Date: Thu, 9 Dec 2021 14:10:25 +0000 Subject: [PATCH 02/73] 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 eaef705074e..a9165dc7adc 100644 --- a/include/seastar/http/exception.hh +++ b/include/seastar/http/exception.hh @@ -41,6 +41,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(); } @@ -52,9 +62,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 ced4b1768b8e3746127a609a4fc30ce1116b77c7 Mon Sep 17 00:00:00 2001 From: John Spray Date: Thu, 9 Dec 2021 14:11:07 +0000 Subject: [PATCH 03/73] 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 1c006e98a48..718f43b59e0 100644 --- a/src/http/routes.cc +++ b/src/http/routes.cc @@ -78,7 +78,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::to_json(e)); + 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::to_json(e)); + } } catch (...) { rep->set_status(http::reply::status_type::internal_server_error, internal::to_json(std::current_exception())); From 9430122c1126149d66f3567590689d83d955d4de Mon Sep 17 00:00:00 2001 From: John Spray Date: Thu, 9 Dec 2021 14:29:14 +0000 Subject: [PATCH 04/73] 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 31376b27d51..2f572a25640 100644 --- a/include/seastar/http/httpd.hh +++ b/include/seastar/http/httpd.hh @@ -115,7 +115,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={}); output_stream& out(); }; diff --git a/src/http/httpd.cc b/src/http/httpd.cc index f4b5f848a3b..3b78c4b81f2 100644 --- a/src/http/httpd.cc +++ b/src/http/httpd.cc @@ -188,11 +188,14 @@ static void set_header_connection(http::reply& resp, bool keep_alive) { } } -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); + } set_header_connection(*resp, false); _done = true; _replies.push(std::move(resp)); @@ -279,7 +282,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 27b7fb28047029812142b7ad3b852045854b278e Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 1 Jun 2022 13:35:42 +0100 Subject: [PATCH 05/73] 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 e47fc035c80..6d8968e1c8d 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -41,6 +41,9 @@ namespace impl { using internalized_labels_ref = lw_shared_ptr; + +int default_handle(); + } } } @@ -230,6 +233,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; @@ -510,7 +515,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 1e3bf662177..e6f041a4239 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -403,13 +403,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()) { @@ -616,6 +620,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 60e86000c67..e48c8cef6d7 100644 --- a/src/core/reactor.cc +++ b/src/core/reactor.cc @@ -124,6 +124,7 @@ #include #include #include +#include #include #include #include @@ -169,6 +170,7 @@ #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 @@ -4061,6 +4063,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 0ee1486b54fda330a26685c017815a25602c51c9 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 22 Jun 2022 17:31:25 +0100 Subject: [PATCH 06/73] 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 6d8968e1c8d..a954850cd6b 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -275,10 +275,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; @@ -510,14 +511,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 @@ -525,7 +527,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()); } @@ -542,7 +544,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 52ef7a30e35..fe3d74c7a71 100644 --- a/include/seastar/core/metrics_registration.hh +++ b/include/seastar/core/metrics_registration.hh @@ -51,12 +51,15 @@ namespace seastar { namespace metrics { namespace impl { +int default_handle(); class metric_groups_def; struct metric_definition_impl; class metric_groups_impl; } +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 e6f041a4239..1806045dc3d 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -39,6 +39,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()) { @@ -110,11 +114,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); }); } @@ -330,15 +334,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); } } @@ -356,14 +360,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; @@ -429,20 +434,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 5d78395d4d40e9e3e603df8a5ddec6d2166ace70 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Mon, 18 Jul 2022 18:58:56 +0100 Subject: [PATCH 07/73] 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 cd3c5749c5c..d65c11d7ebd 100644 --- a/include/seastar/core/metrics.hh +++ b/include/seastar/core/metrics.hh @@ -435,6 +435,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; }; escaped_string shard(); diff --git a/include/seastar/core/metrics_api.hh b/include/seastar/core/metrics_api.hh index a954850cd6b..584cc29632c 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -286,6 +286,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 1806045dc3d..53c36708721 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -388,6 +388,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 e73a347c7efd83f9fd77403780f1449d9c2c523d Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 22 Jun 2022 17:34:52 +0100 Subject: [PATCH 08/73] 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 fe3d74c7a71..46a37cd5fec 100644 --- a/include/seastar/core/metrics_registration.hh +++ b/include/seastar/core/metrics_registration.hh @@ -93,7 +93,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; @@ -102,7 +102,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. @@ -158,7 +158,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(); @@ -169,7 +169,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()); }; diff --git a/src/core/io_queue.cc b/src/core/io_queue.cc index 696faf94117..f6eddc27587 100644 --- a/src/core/io_queue.cc +++ b/src/core/io_queue.cc @@ -861,7 +861,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 53c36708721..183fcd267a7 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -45,14 +45,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); } @@ -65,10 +66,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 e48c8cef6d7..3790daf2e1c 100644 --- a/src/core/reactor.cc +++ b/src/core/reactor.cc @@ -1061,7 +1061,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 151823d049c01bbc301b0651966d3b85792d6688 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 22 Jun 2022 14:06:39 +0100 Subject: [PATCH 09/73] 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 183fcd267a7..9b97b57a6e9 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -450,8 +450,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 aa90df43c1519a7a3b1f2ec256e32e34580a5b4a Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 1 Jun 2022 13:42:25 +0100 Subject: [PATCH 10/73] 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 | 15 +++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/include/seastar/core/prometheus.hh b/include/seastar/core/prometheus.hh index 6144daa87c1..f9e225ce723 100644 --- a/include/seastar/core/prometheus.hh +++ b/include/seastar/core/prometheus.hh @@ -55,12 +55,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(sharded& 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 7b8b6115ceb..27bbdad9dc0 100644 --- a/src/core/prometheus.cc +++ b/src/core/prometheus.cc @@ -561,17 +561,15 @@ 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(this_smp_shard_count()); - co_await parallel_for_each(std::views::iota(0u, this_smp_shard_count()), [&vec] (auto cpu) { - return smp::submit_to(cpu, [] { - return mi::get_values(); + co_await parallel_for_each(std::views::iota(0u, this_smp_shard_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); }); }); - co_return vec; } /*! @@ -1111,7 +1109,8 @@ class metrics_handler : public httpd::handler_base { future<> write_body(write_body_args args, output_stream&& out_stream) { auto s = std::move(out_stream); - auto families = co_await get_map_value(); + metrics_families_per_shard families; + co_await get_map_value(families, _ctx.handle); bool use_protobuf = args.use_protobuf_format; write_context context{ @@ -1138,7 +1137,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 3f737ea100f92bebf7597042821945314a05e586 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Wed, 1 Jun 2022 13:42:57 +0100 Subject: [PATCH 11/73] 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 cab55657f8b..ce1d283c99a 100644 --- a/include/seastar/core/scollectd.hh +++ b/include/seastar/core/scollectd.hh @@ -371,7 +371,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; @@ -390,8 +390,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(); @@ -779,8 +779,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 7e20dccc2ab..fc5d82de8cd 100644 --- a/src/core/scollectd.cc +++ b/src/core/scollectd.cc @@ -84,12 +84,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) { @@ -542,7 +542,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; @@ -551,7 +551,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 55dc12bba5390159bf27b50ce8a1df75931b2ca4 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Thu, 7 Jul 2022 11:52:21 +0100 Subject: [PATCH 12/73] 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 584cc29632c..c4d66179208 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -255,6 +255,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 21794473ae33f2dd1e923d360955cae76cbc9749 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Tue, 12 Jul 2022 12:24:50 +0100 Subject: [PATCH 13/73] 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 c4d66179208..dde9083d018 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -463,6 +463,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; @@ -504,6 +505,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; } @@ -515,6 +517,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 9b97b57a6e9..61d3da3a8c5 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -478,6 +478,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 6fd85c4d071c9638fd12fcb9ac4dd918603ada26 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Mon, 11 Jul 2022 12:43:58 +0100 Subject: [PATCH 14/73] 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 dde9083d018..aea384154b0 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -524,6 +524,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 61d3da3a8c5..70af470767a 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -438,6 +438,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 69a47620479704ea58e6aa379075352295600fda Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Tue, 12 Jul 2022 12:27:41 +0100 Subject: [PATCH 15/73] 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 aea384154b0..54ddfd8324b 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -514,6 +514,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 70af470767a..0987e909e53 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -508,6 +508,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 f66547c23998e4469cdcc3a418e0e58b5040c2e7 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Mon, 11 Jul 2022 12:44:22 +0100 Subject: [PATCH 16/73] 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 0987e909e53..e2e6fec9986 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -424,6 +424,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()); @@ -645,6 +647,8 @@ register_ref impl::add_registration(const metric_id& id, const metric_type& type } dirty(); + replicate_metric_if_required(rm); + return rm; } From f70c6f8dc72162984e8033b85da5939551142614 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Tue, 12 Jul 2022 12:41:42 +0100 Subject: [PATCH 17/73] 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 54ddfd8324b..2daf5457f3d 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -659,5 +659,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 e2e6fec9986..743f2745080 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -201,6 +201,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 6db1233d3d7302adb7b3b53ab3035da9392f4a74 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Mon, 11 Jul 2022 12:49:33 +0100 Subject: [PATCH 18/73] 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 89d14282d16..3a1ca7ada17 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -445,6 +445,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..584ba13f479 --- /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 ff56a8376b7b95721c5aff58bd48d9cad7eb92ea Mon Sep 17 00:00:00 2001 From: Stephan Dollberg Date: Thu, 19 Oct 2023 10:30:21 +0100 Subject: [PATCH 19/73] 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 d65c11d7ebd..ba25db36a52 100644 --- a/include/seastar/core/metrics.hh +++ b/include/seastar/core/metrics.hh @@ -650,6 +650,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