Skip to content

Record cluster worker scrape failures - #850

Open
nightjard wants to merge 2 commits into
prometheus:mainfrom
nightjard:fix/cluster-worker-failure-histogram-290
Open

Record cluster worker scrape failures#850
nightjard wants to merge 2 commits into
prometheus:mainfrom
nightjard:fix/cluster-worker-failure-histogram-290

Conversation

@nightjard

Copy link
Copy Markdown

Summary

Add an internal histogram named
prom_client_cluster_worker_scrape_failures to record failed cluster metric
collections.

Each observation contains the number of workers that failed to return metrics:

  • For timeouts, it records the number of outstanding worker responses.
  • For worker-reported errors, it records the known worker failure count.
  • Failed collections continue to reject without returning partial metrics.
  • New observations are exposed on the next successful clusterMetrics() call.

A histogram preserves information from consecutive failures instead of only
retaining the most recent value, as a gauge would.

Testing

Added tests covering:

  • Multiple workers timing out.
  • Worker-reported collection errors.
  • Histogram exposure after a successful recovery scrape.
  • Prometheus and OpenMetrics output formats.

Signed-off-by: SirGundir <crstalhopkin@gmail.com>
@nightjard
nightjard force-pushed the fix/cluster-worker-failure-histogram-290 branch from 94a6e40 to 801c14c Compare September 4, 2026 19:31
Comment thread lib/cluster.js Outdated
}

if (failedWorkers > 0) {
clusterWorkerScrapeFailures.observe(failedWorkers);

@jdmarshall jdmarshall Sep 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think observing 0's when there are no workers is probably unlikely to happen but is also extra information about the class of failure we may be dealing with. As written this will never catch unclassified errors such as an NPE in the try block.

For instance, regressions in the Node API are not unknown. I work on a project that doesn't work on node 24.7-24.16, for instance, due to breaking changes in child_process.fork()

@jdmarshall jdmarshall left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This same change should be made in worker.js as well, please, to retain feature parity.

Comment thread lib/cluster.js Outdated
const metrics = await Promise.all(
registries.map(r => r.getMetricsAsJSON()),
);
metrics.push([await clusterWorkerScrapeFailures.get()]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure you're not double-reporting this? That should be in the default registry.

Record unclassified failures, add worker-thread parity, register the histograms in the default registry, and distinguish scrape deadlines from collector errors with the same message. Add regression tests and a changelog entry.

Signed-off-by: SirGundir <crstalhopkin@gmail.com>
@nightjard

Copy link
Copy Markdown
Author

Thanks for the review! I've added worker-thread support, zero-valued failure observations, and default-registry registration without double-counting. I also fixed the Timeout error mix-up and added the changelog entry. All 633 tests pass locally

Comment thread lib/cluster.js
);

const responsePromises = [this.#selfMetrics(), ...workerMetrics];
const timeoutError = new Error('Timeout');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can get a bit weird with stack traces. I think you're better off just letting waitFor create the error rather than passing one around.

Comment thread lib/cluster.js
);
// Custom registries may not contain the coordinator's internal metric.
if (
!registries.some(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if it's being added to the registry you don't need this check.

Comment thread lib/worker.js
} else {
debug('No workers found for requestId', requestId);
}
debug('No workers found for requestId', requestId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change?

Comment thread lib/worker.js

workerScrapeFailures.observe(failedWorkers);

// Sending can throw before request.promise is awaited.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an AI hallucination?

You're rejecting and then squelching the error handler after rejection? I don't think we need any of this.

Comment thread lib/worker.js
}
}

async #selfMetrics() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also not needed. This thread is listening for the broadcast channel that sends the metrics. So again double reporting.

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 this pull request may close these issues.

2 participants