Skip to content

feat: per-scraper query timeout to prevent slow scraper from cascading-failing the rest#1025

Open
AadiSanghani wants to merge 3 commits into
prometheus:mainfrom
Shopify:make-exporter-more-resilient
Open

feat: per-scraper query timeout to prevent slow scraper from cascading-failing the rest#1025
AadiSanghani wants to merge 3 commits into
prometheus:mainfrom
Shopify:make-exporter-more-resilient

Conversation

@AadiSanghani

@AadiSanghani AadiSanghani commented May 5, 2026

Copy link
Copy Markdown

Summary

A single slow scraper currently causes every other scraper to fail on the same /metrics request. Two coupled changes fix this:

  1. Per-scraper context timeout — each scraper gets its own context.WithTimeout(parent, --exporter.query_timeout) instead of sharing the request context. A new --exporter.query_timeout flag controls
    the timeout. Default is 0 (disabled), so existing deployments see no behavior change.

  2. Connection pool capacityMaxOpenConns raised from 1 to 2 and ConnMaxLifetime set to 1m. With only one connection, scrapers serialize behind a slow query and timeout while waiting for the
    connection rather than while running, which the per-scraper timeout cannot catch on its own.

The instance.Ping() call also takes a context now and uses db.PingContext so the ping respects the same timeout.

Why both changes are needed

Per-scraper timeout alone is not sufficient. With MaxOpenConns(1), fast scrapers queue behind a slow one waiting for the connection, then hit their per-scraper deadline while still in the queue and fail
with context deadline exceeded. Bumping the pool to 2 lets the fast scrapers run on a second connection while the slow one is in flight, so the per-scraper timeout fires only on the genuinely slow
scraper.

Reproduction

A scraper that runs SELECT SLEEP(15) was added locally to reproduce the failure mode against MySQL 8.4 with a 5s scrape budget (X-Prometheus-Scrape-Timeout-Seconds: 5).

Configuration Scrape time Failed scrapers Notes
MaxOpenConns=1, no per-scraper timeout (pre-fix) 4.8s 3 of 7 slow + 2 queued behind it
MaxOpenConns=2 + --exporter.query_timeout=3 (post-fix) 3.0s 1 of 7 only the slow scraper

Pre-fix uses the entire scrape budget regardless of what's actually slow; post-fix uses exactly the per-scraper timeout that's configured.

Also full credit to @joshbroughton for this PR.

@AadiSanghani AadiSanghani force-pushed the make-exporter-more-resilient branch from 0b4b13a to 3946eb0 Compare May 5, 2026 15:44
@AadiSanghani AadiSanghani marked this pull request as ready for review May 5, 2026 15:52
@AadiSanghani

Copy link
Copy Markdown
Author

cc: @SuperQ

@AadiSanghani AadiSanghani force-pushed the make-exporter-more-resilient branch from 3946eb0 to 438876e Compare June 5, 2026 18:17
Comment thread collector/instance.go Outdated
db.SetMaxOpenConns(1)
db.SetMaxOpenConns(2)
db.SetMaxIdleConns(1)
db.SetConnMaxLifetime(1 * time.Minute)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'm wondering if these changes are related and required to the main idea behind this PR.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed the db.SetConnMaxLifetime(1 * time.Minute) as its not needed for this PR.

Comment thread collector/exporter.go Outdated

if err := instance.Ping(); err != nil {
pingCtx := ctx
if e.queryTimeout > 0 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'm wondering if we can consolidate this check queryTimeout > 0 and thinking if we really need a context for ping and another for scrape, since they are using the same timeout value.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fair! I moved them out to its method now.

@nicolastakashi

Copy link
Copy Markdown

@AadiSanghani can you check DCO 🙏🏽

Comment thread collector/exporter.go Outdated
@AadiSanghani AadiSanghani force-pushed the make-exporter-more-resilient branch 2 times, most recently from 3ff2bb6 to 25add20 Compare July 6, 2026 14:02
AadiSanghani and others added 3 commits July 6, 2026 10:10
Signed-off-by: Aadi Sanghani <aadi.sanghani@shopify.com>
Signed-off-by: Aadi Sanghani <aadi.sanghani@shopify.com>
Co-authored-by: Nicolas Takashi <nicolas.tcs@hotmail.com>
Signed-off-by: Aadi Sanghani <42876004+AadiSanghani@users.noreply.github.com>
Signed-off-by: Aadi Sanghani <aadi.sanghani@shopify.com>
@AadiSanghani AadiSanghani force-pushed the make-exporter-more-resilient branch from 25add20 to 74a9ded Compare July 6, 2026 14:12
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