Skip to content

fix(metrics): record watchable_depth as the coalesced update count - #9860

Open
somaz94 wants to merge 1 commit into
envoyproxy:mainfrom
somaz94:fix/watchable-depth-metric
Open

fix(metrics): record watchable_depth as the coalesced update count#9860
somaz94 wants to merge 1 commit into
envoyproxy:mainfrom
somaz94:fix/watchable-depth-metric

Conversation

@somaz94

@somaz94 somaz94 commented Aug 27, 2026

Copy link
Copy Markdown

What this PR does / why we need it:

watchable_depth records len(subscription), where subscription is the channel
returned by watchable.Map.Subscribe(). That channel is unbuffered
(downstream := make(chan Snapshot[K, V]) in the watchable library), so the gauge
can only ever read 0.

It isn't dead weight: the shipped Grafana dashboard graphs it, so a flat zero reads
as "no backlog" while the control plane can be seconds behind.

This records len(snapshot.Updates) instead. The watchable library documents
Snapshot.Updates as "the list of mutations that have happened since the previous
snapshot", accumulated while the consumer is busy, so its length is the backlog that
built up during the previous handler pass. It's always available, unlike
watchable_debounce_pending, which only reports when debouncing is on.

The pre-coalesce count is deliberate. coalesceUpdates dedupes by key, so its
result measures the work left to do while the raw count measures how much arrived.
Happy to switch to the coalesced count if you'd rather the gauge track that.

The metric name and labels are unchanged, so the dashboard's
label_values(watchable_depth, ...) template variables keep working and the depth
panel becomes meaningful rather than needing to be repointed. Anything that treated
the constant 0 as meaningful will now see real values, hence the breaking-change
note.

Validation, run locally on darwin/arm64 with the go.mod-pinned Go 1.26.6:

  • make lint passes (golangci-lint, yamllint, codespell, shellcheck, release-notes
    filename check).
  • make gen-check passes and leaves the tree clean.
  • go test ./internal/message/... ./internal/metrics/... passes.
  • The new test fails with actual: 0 when the old len(subscription) line is put
    back, which is the bug it's pinning.

Which issue(s) this PR fixes:

Fixes #9776

Item 2 of that issue (subscribe-duration bucket spacing) already landed in #9812;
this is item 1.


PR Checklist

  • Authorship & ownership: Coding agents / AI assistants are welcome, but I have reviewed every change, understand how and why it works, can explain and maintain it, and take full responsibility for this PR. I have not submitted generated output I do not understand.
  • DCO: All commits are signed off (git commit -s).
  • API agreed first: N/A: no changes under /api.
  • Required checks pass: make lint and make gen-check pass locally, and the unit tests for the touched packages pass.
  • Tests added/updated: TestHandleSubscriptionRecordsCoalescedUpdateCount in internal/message/watchutil_internal_test.go.
  • Docs: updated the watchable_depth row in site/content/en/latest/tasks/observability/gateway-exported-metrics.md.
  • Release notes: release-notes/current/breaking_changes/9776-watchable-depth-coalesced-updates.md.
  • Generated files committed: make gen-check is clean.
  • Scope & compatibility: one metric, its doc row and its release note. The semantic change is called out above and in release-notes/current/breaking_changes/.
  • Codex review: Requested a Codex review and addressed all of its comments.
  • Copilot review: Requested a Copilot review and addressed all of its comments.

On the last two boxes: I can't request either reviewer myself — as an external
contributor I don't have push or triage on this repo, so --add-reviewer returns
404, and Copilot doesn't appear to be set up here. Codex looks like it skips
drafts, so I'm marking this ready for it to pick up, and I'll tick that box once
I've addressed its comments.

Signed-off-by: somaz <genius5711@gmail.com>
@netlify

netlify Bot commented Aug 27, 2026

Copy link
Copy Markdown

Deploy Preview for cerulean-figolla-1f9435 ready!

Name Link
🔨 Latest commit 0f90d80
🔍 Latest deploy log https://app.netlify.com/projects/cerulean-figolla-1f9435/deploys/6a8fb27c24367f00084391fc
😎 Deploy Preview https://deploy-preview-9860--cerulean-figolla-1f9435.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0f90d808a0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// batch was being handled, so its length is the backlog that built up during
// that pass. len(subscription) cannot report that: Subscribe returns an
// unbuffered channel, so its length is always 0.
watchableDepth.With(meta.LabelValues()...).Record(float64(len(snapshot.Updates)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reset depth after finishing each snapshot

When a nonempty snapshot finishes processing and the publisher becomes idle—or the subscription closes—this remains at len(snapshot.Updates) indefinitely because Gauge.Record stores the last value (internal/metrics/otel_metric_gauge.go:30-38) and no later zero is recorded. Consequently, the shipped Grafana depth panel (charts/gateway-addons-helm/dashboards/envoy-gateway-global.json:277) can show a permanent backlog, and alerts can remain active, even though no snapshot is currently being handled. Reset the series after the batch completes, or use a metric whose documented semantics explicitly represent the last observed batch rather than current depth.

Useful? React with 👍 / 👎.

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.

Improve the watchable control-plane metrics: watchable_depth is always 0, and subscribe duration buckets are too coarse below 10s

1 participant