Skip to content

Exporter opens a new MongoDB connection on every scrape by default #1346

Description

@ademidoff

Describe the bug

By default the exporter builds a brand-new mongo.Client for every scrape and disconnects it when the scrape finishes. Each Prometheus scrape therefore pays for a full TCP connect, TLS handshake (when enabled), SCRAM authentication, and server/topology discovery against the monitored instance.

--mongodb.global-conn-pool exists to avoid this, but it is opt-in: at main.go:47 the flag carries no default: tag, so it defaults to false. (Compare --mongodb.direct-connect on the following line, which does declare default:"true".)

Where

  • exporter/exporter.go:296 — the non-pooled branch of getClient, comment and all:
    // !e.opts.GlobalConnPool: create new client for every scrape.
    client, err := connect(ctx, e.opts)
  • exporter/exporter.go:423connect() performs mongo.Connect followed by client.Ping.
  • exporter/exporter.go:341 — the deferred client.Disconnect(ctx) that tears it back down at the end of the scrape.
  • exporter/server.go:134/scrapeall repeats the same connect/disconnect cycle per exporter.

Why it matters

SCRAM is intentionally expensive: the server runs the configured iteration count on every authentication. At a 15s scrape interval that is roughly 5,760 full authentication handshakes per target per day, and the CPU cost lands on the monitored mongod, not on the exporter. Add TLS handshakes and repeated topology discovery on top. Connection churn also inflates mongod connection counters and can trip connection-rate alerting.

Expected behavior

Steady-state scraping should reuse a connection. Open questions for whoever picks this up:

  1. Should --mongodb.global-conn-pool default to true? That is a behavior change for existing deployments and needs a deliberate call plus a release note, but reconnect-per-scrape is a surprising default for a Prometheus exporter.
  2. If the default stays false, the flag at minimum deserves a prominent mention in README.md — today its cost is invisible to operators.
  3. The pooled path may need hardening first. In getClient (exporter/exporter.go:278), when the cached client's Ping fails the function returns an error but leaves e.client populated and never rebuilds it. The Go driver recovers from transient topology failures on its own, so this self-heals in the common case, but it is worth confirming there is no state (rotated credentials, for example) where the exporter stays wedged until restart. That question should be settled before the pooled path becomes the default.

Related

Environment

Not environment-specific; applies to all builds from main (verified @ 9585218).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions