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.

(cherry picked from commit 1cf66d1c67)
This commit is contained in:
Michael Vogt
2026-10-16 11:36:32 +01:00
committed by Luca Boccassi
parent 97b3c393bf
commit 2bd3ff6e81
+26 -12
View File
@@ -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");