From 1cf66d1c674ce5928f5b2709629afeaaaf79aee2 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 17 Sep 2026 08:42:58 +0200 Subject: [PATCH] report: replace METRICS_MAX with a per source limit The existing limit for the reports was a global METRICS_MAX cap. However on systems with a verbose socket or with many sockets that is easily hit and because sockets are contacted in parallel random parts of the following report generators are skipped. It seems better to instead make the limit per-source so that one that is overly verbose cannot starve the others. Because we already have METRICS_LINKS_MAX this does not add a global limit - we could add one but it would have the same problem as METRICS_MAX before, some report would be cut that might be important (or we need to be explicit about ordering but even then its problematic if we have two "important" ones and one is buggy and generates too much data). Note that this keeps the METRICS_PER_SOURCE_MAX at 4096 because io.systemd.Manager reports a lot, on my idle test VM already ~3k. We also now report any issue with the metric instead of just the first one in verb_metrics. --- src/report/report.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/report/report.c b/src/report/report.c index f7c0c35207..b3c24572be 100644 --- a/src/report/report.c +++ b/src/report/report.c @@ -9,6 +9,7 @@ #include "chase.h" #include "dirent-util.h" #include "dlopen-note.h" +#include "errno-util.h" #include "format-table.h" #include "json-util.h" #include "log.h" @@ -33,7 +34,8 @@ #include "verbs.h" #include "web-util.h" -#define METRICS_MAX 4096U +/* We limit per source, with the METRICS_LINKS_MAX this gives us a global cap too */ +#define METRICS_PER_SOURCE_MAX 4096U #define METRICS_LINKS_MAX 128U #define TIMEOUT_USEC (30 * USEC_PER_SEC) /* 30 seconds */ @@ -66,6 +68,8 @@ typedef struct LinkInfo { Context *context; sd_varlink *link; char *name; + size_t n_metrics; + bool logged_metrics_per_source_max_reached; } LinkInfo; static LinkInfo* link_info_free(LinkInfo *li) { @@ -254,7 +258,13 @@ static int on_query_reply( goto finish; } - if (context->n_metrics >= METRICS_MAX) { + if (li->n_metrics >= METRICS_PER_SOURCE_MAX) { + if (!li->logged_metrics_per_source_max_reached) { + li->logged_metrics_per_source_max_reached = true; + log_warning("Reached limit of %u metrics, dropping further metrics from '%s'.", + METRICS_PER_SOURCE_MAX, li->name); + } + context->n_skipped_metrics++; goto finish; } @@ -272,6 +282,7 @@ static int on_query_reply( return log_oom(); context->metrics[context->n_metrics++] = sd_json_variant_ref(parameters); + li->n_metrics++; finish: if (!FLAGS_SET(flags, SD_VARLINK_REPLY_CONTINUES)) { @@ -716,19 +727,22 @@ static int verb_metrics(int argc, char *argv[], uintptr_t data, void *userdata) return r; } + r = 0; + if (context.n_skipped_sources > 0) - return log_warning_errno(SYNTHETIC_ERRNO(EUCLEAN), - "Too many metrics sources, only %zu sources contacted, %zu sources skipped.", - context.n_contacted_sources, context.n_skipped_sources); + RET_GATHER(r, log_warning_errno(SYNTHETIC_ERRNO(EUCLEAN), + "Too many metrics sources, only %zu sources contacted, %zu sources skipped.", + context.n_contacted_sources, context.n_skipped_sources)); if (context.n_invalid_metrics > 0) - return log_warning_errno(SYNTHETIC_ERRNO(EUCLEAN), - "%zu metrics are not valid.", - context.n_invalid_metrics); + RET_GATHER(r, log_warning_errno(SYNTHETIC_ERRNO(EUCLEAN), + "%zu metrics are not valid.", + context.n_invalid_metrics)); if (context.n_skipped_metrics > 0) - return log_warning_errno(SYNTHETIC_ERRNO(EUCLEAN), - "Too many metrics, only %zu metrics collected, %zu metrics skipped.", - context.n_metrics, context.n_skipped_metrics); - return 0; + RET_GATHER(r, log_warning_errno(SYNTHETIC_ERRNO(EUCLEAN), + "Too many metrics, only %zu metrics collected, %zu metrics skipped.", + context.n_metrics, context.n_skipped_metrics)); + + return r; } VERB_NOARG(verb_list_sources, "list-sources", "Show list of known metrics sources");