Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling GET /metrics results in clearing all cached metrics #3

Open
MeenaAlfons opened this issue Apr 15, 2023 · 2 comments · Fixed by #4
Open

Calling GET /metrics results in clearing all cached metrics #3

MeenaAlfons opened this issue Apr 15, 2023 · 2 comments · Fixed by #4

Comments

@MeenaAlfons
Copy link
Contributor

MeenaAlfons commented Apr 15, 2023

This snippet is taken from reportMetrics() which is called upon receiving a request to GET /metrics.

// TODO: skip too-old metrics (report to process output), it confuses prometheus
// TODO: hold "too new" samples for the next scrape (ie samples that are from the next cluster)
// TODO: report metrics with a lag (guard band), to not split clusters of samples
samples = samples.splice(0);
// discard samples older than the configured cutoff
if (this.maxMetricAgeMs >= 0) {
var now = this.getTimestamp();
var oldestKeepTs = now - this.maxMetricAgeMs;
for (i=0; i<samples.length && samples[i].ts < oldestKeepTs; i++) ;
if (i > 0) {
Gateway.trace("discarding %d samples older than %d ms, from %d to %d",
i, this.maxMetricAgeMs, samples[0].ts, samples[i-1].ts);
samples = samples.slice(i);
}
}

In this part, samples.splice(0) results in removing all stored samples in this.samples. I don't get why this is needed. There is already a mechanism to get rid of old samples using maxMetricAgeMs.

Here is more context on why it doesn't make sense to remove cached metrics:

  • prom-pushgateway should serve as a caching layer.
  • Metrics will be pushed to prom-pushgateway. pushing a new value for metric_1{<label_set>} should overwrite any previous value for that metric and label set. However, pushing a new metric metric_2 should not result in removing metric_1.
  • Reporting metrics via GET /metrics should not affect the cached metrics.
  • The line samples.splice(0) results in clearing all cached metrics when GET /metrics is called!

I appreciate your inputs on this and I'm happy to open a PR with a solution.

@MeenaAlfons
Copy link
Contributor Author

After reading the rest of the code, I found out that older values for metrics are kept in this.previousValues which acts as the cache in this case. samples are just the set of samples since the last (scrape) call to reportMetrics. Those samples are averaged and added to the cached values.

In that since, I believe maxMetricAgeMs doesn't actually do what it suggests. The old metrics in this.previousValues are never checked or removed.

@MeenaAlfons
Copy link
Contributor Author

I think we need to change the following code with the code suggested afterwards.

Current code:

// never omit any known metrics, report the previous value if no change
// This allows /metrics to be called more frequently than the /push interval.
var allValues = {};
for (id in this.previousValues) allValues[id] = this.previousValues[id];
for (id in map) allValues[id] = map[id];
this.previousValues = allValues;

Suggested code:

    // never omit any known metrics, report the previous value if no change
    // This allows /metrics to be called more frequently than the /push interval.
    var allValues = {};
    const now = this.getTimestamp();
    const oldestKeepTs = this.maxMetricAgeMs >= 0 ? (now - this.maxMetricAgeMs) : 0;
    const filterOldMetrics = this.maxMetricAgeMs >= 0;
    for (id in this.previousValues) {
        if (!filterOldMetrics || this.previousValues[id].ts >= oldestKeepTs) {
            allValues[id] = this.previousValues[id];
        }
    }
    const discardedMetrics = Object.keys(this.previousValues).length - Object.keys(allValues).length;
    if (discardedMetrics > 0) {
        Gateway.trace("discarding %d metrics older than %d ms", discardedMetrics, this.maxMetricAgeMs);
    }
    for (id in map) allValues[id] = map[id];
    this.previousValues = allValues;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant