Skip to content

fix(metrics): bound the instance metrics Collect call - #1177

Open
abd-ulbasit wants to merge 1 commit into
cloudnative-pg:mainfrom
abd-ulbasit:dev/1045
Open

abd-ulbasit wants to merge 1 commit into
cloudnative-pg:mainfrom
abd-ulbasit:dev/1045

Conversation

@abd-ulbasit

Copy link
Copy Markdown

Refs #1045

This change puts a deadline on plugin metrics collection and removes the global client lock from API-server requests.

What changed

  • Collect now has its own 5-second deadline. If the client doesn't respond in time, collection returns context.DeadlineExceeded. The instance manager can record the collection error and continue serving the remaining metrics.
  • ExtendedClient no longer holds its mutex while waiting for the API server. The cache is checked under the lock, the request runs without it, and the result is written back under the lock.

Why

A single slow API-server read shouldn't be able to stall the plugin's metrics indefinitely or block unrelated cached reads. The goal is to limit the impact to the plugin's own metrics for that scrape, rather than holding up the instance's entire /metrics endpoint.

Scope

This PR addresses the plugin-side collection and cache behavior only. It doesn't add a deadline to the core pluginCollector.Collect call; that belongs in cloudnative-pg/cloudnative-pg. It also doesn't add a retention-cycle timeout, which needs exec.CommandContext support in barman-cloud first (#1044).

Cache behavior

Allowing concurrent cache misses means two requests for the same entry can reach the API server at the same time. Each entry is timestamped using the time its request was sent, and an older request completing late must not overwrite a newer cache entry. Entries also remain subject to the existing 10-second TTL measured from the request's start time.

Tests

  • Added a test verifying that Collect returns DeadlineExceeded when the client never responds.
  • Added a test verifying that a cached Get isn't blocked by another Get stuck on the API server.
  • Added a test verifying that a slow request completing last doesn't overwrite a newer cached entry.
  • On the base branch, the first two tests time out after 2 seconds, and the third fails without the check that preserves the newer cache entry. All three pass with this change.
  • go test (non-e2e): passed.
  • go test -race on internal/cnpgi/instance/...: passed.
  • golangci-lint v2.15.0: passed.

Disclosure: parts of this change and its tests were written with Claude Code (Claude Opus 5.5), as noted in the Assisted-by commit trailer.

The instance manager calls the plugin Collect RPC while it serves the
/metrics endpoint of the instance, and it does not set a deadline. If
Collect never returns, the whole endpoint hangs, not only the plugin
metrics.

Give Collect its own 5 second deadline, so it returns an error instead.
The instance manager then logs the error, counts it in
cnpg_collector_collection_errors_total, and still serves the other
metrics.

The deadline alone is not enough. The caching client held its lock for
the whole request to the API server, and a mutex does not honour a
context. One slow Secret or ObjectStore read blocked every other cached
read, including the one in Collect. Release the lock while the request
is in flight.

Two lookups of the same object can now run at the same time. Stamp each
cache entry with the time its request was sent, and do not replace an
entry that was fetched later, so a slow request cannot overwrite fresher
data or keep old data past the TTL.

Refs cloudnative-pg#1045

Assisted-by: Claude Opus 5.5
Signed-off-by: abd-ulbasit <basit@basit.engineer>
@abd-ulbasit
abd-ulbasit requested a review from a team as a code owner October 21, 2026 18:38

This branch has not been deployed

No deployments
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.

1 participant