performance: add opt-in debounce for resource updates - #9773
Conversation
✅ Deploy Preview for cerulean-figolla-1f9435 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #9773 +/- ##
==========================================
+ Coverage 76.30% 76.32% +0.02%
==========================================
Files 261 261
Lines 44351 44467 +116
==========================================
+ Hits 33842 33940 +98
- Misses 8271 8288 +17
- Partials 2238 2239 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
ec35452 to
71d0d69
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8975c30dfd
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
8975c30 to
4f30300
Compare
Merge bursts of resource changes into a single translation instead of translating and pushing each one, bounding how often configuration is pushed to Envoy under heavy EndpointSlice churn. Configured via spec.debounce and disabled by default. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
Give the debounce-aware entry point a *DebounceOptions parameter so callers can pass nil, and keep HandleSubscription as a thin wrapper over it. The gateway-api call site then needs no branch and keeps its handler inline, and no other caller changes. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
Describe the cost of undebounced updates without naming a particular resource type or asserting how translation is implemented, and drop a comment that only restated the code. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
Add watchable_coalesced_updates_total so the updates dropped by coalescing can be observed at any log level, not only where the existing info log is enabled. Name the snapshot channel for what it carries, drop a redundant Stop before Reset, and stop recording watchable_depth on the debounced path, where it is always zero. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
4f30300 to
46b07ed
Compare
Follow how xdsServer durations are handled: the validation package owns the rules, and the runner parses the strings where it uses them. This drops DebounceSettings, which was the only method in the EnvoyGateway helpers that validated its input and returned an error. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
# Conflicts: # internal/message/metrics.go # internal/message/watchutil.go
Envoy Gateway coalesces updates today only by backpressure — the watchable library merges stores that pile up while a consumer is busy — plus last-write-wins within a single snapshot. Neither is time-based, so when translation is fast but resources are churning, each change costs its own translation and its own push to Envoy, even though only the resulting state matters.
This PR adds a time-based debounce, configured via the top-level
debouncefield in the EnvoyGateway config and disabled by default. A pending batch is flushed once no new change has arrived fordebounce.after(default 100ms), or afterdebounce.max(default 10s) when changes keep arriving, so isolated changes still propagate promptly while sustained churn has a bounded push rate.The debounce sits on the gateway-api runner's subscription, ahead of
TranslateToIR, so it collapses churn before both the Gateway API and the xDS translation.Measured
Scaling a backend Deployment 180 times between 1 and 20 replicas, 100ms apart, against a single proxy:
A 44% raw reduction in pushes to Envoy, or 48% after normalizing for the slightly higher input volume.
watchable_coalesced_updates_totalreported 142, exactly the 296 − 154 difference. Mean hold time was 159ms, and every flush wasreason="quiet", somaxwas never reached.Two caveats on reading that. The churn interval here (100ms) equals the default
after, which is close to the least favourable spacing for a debouncer — each change tends to arrive just as the quiet period expires, and the average batch was only 1.92 updates. Faster churn coalesces more; churn slower thanaftercoalesces nothing. And this ran against a single Envoy, whilexds_snapshot_update_totalis counted per node, so absolute savings scale with fleet size.Churn script: https://gist.github.com/zhaohuabing/33a57e7b6fd3126c17001c3208d288b2
Observability
The
watchable_subscribe_duration_secondsbuckets topped out at 10s, which hid the tail where a single translation takes tens of seconds; they now extend to 120s, keeping the existing boundaries so the change is additive. New metricswatchable_debounce_pending,watchable_debounce_flush_totalandwatchable_debounce_delay_secondscover the debouncer, andwatchable_coalesced_updates_totalcounts the updates dropped by coalescing — previously visible only in an info-level log line, which is of no use on a control plane configured to log aterror.Note that
watchable_depthis always 0, because snapshots are delivered over an unbuffered channel. That is pre-existing and left alone here, but documented so it is not mistaken for a healthy queue.Alternative
Debouncing the kubernetes provider reconcile - it avoids churns at the source, but may delay Gateway status update and won't hanlde other providers.