Skip to content

performance: opt-in debounce for the provider reconcile - #9803

Open
zhaohuabing wants to merge 1 commit into
envoyproxy:mainfrom
zhaohuabing:provider-reconcile-debounce
Open

performance: opt-in debounce for the provider reconcile#9803
zhaohuabing wants to merge 1 commit into
envoyproxy:mainfrom
zhaohuabing:provider-reconcile-debounce

Conversation

@zhaohuabing

@zhaohuabing zhaohuabing commented Aug 20, 2026

Copy link
Copy Markdown
Member

Envoy Gateway coalesces reconcile requests today only by workqueue dedup — every watch enqueues the same GatewayClass request, so events arriving while a reconcile is in flight collapse into a single requeue when Done is called. That is not time-based, so the coalescing window is incidentally whatever one reconcile happens to take: when reconciles are fast relative to the event rate the queue drains between events, and each event then costs a full rebuild of the resource tree even though only the resulting state matters.

This PR adds a time-based debounce, configured via the same top-level debounce field in the EnvoyGateway config as #9773 and disabled by default. A pending batch is flushed once no new request has arrived for debounce.after (default 100ms), or after debounce.max (default 10s) when requests keep arriving, so isolated changes still reconcile promptly while sustained churn has a bounded reconcile rate.

The debounce sits on the controller's workqueue, through the existing NewQueue hook, so it collapses churn ahead of Reconcile itself — the cache Lists and the resource-tree rebuild — and therefore ahead of everything downstream of the provider publish too. Only Add is debounced: AddAfter and AddRateLimited reach the embedded queue directly, so error-requeue backoff is never delayed.

Because status is computed during Reconcile, enabling this also delays Gateway and policy status by up to debounce.max. That is the main tradeoff against #9773, which leaves status untouched.

Measured

Scaling a backend Deployment 180 times between 1 and 20 replicas, 100ms apart, against a single proxy:

disabled enabled (defaults)
reconcile inputs 299 299
reconcile debounce flushes 0 158
provider publishes 299 158
snapshots created 299 158
snapshot updates pushed 299 158
pushes per input reconcile 1.000 0.528

A 47% raw reduction in pushes to Envoy: 299 -> 158, or 141 fewer snapshot updates.

The provider publish number is lower than the #9773 result because this PR debounces earlier: it wraps the provider controller reconcile queue before provider-resources is published.

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 near the quiet-period boundary, so faster churn should coalesce more and slower churn should coalesce little or nothing. And this ran against a single Envoy, while xds_snapshot_update_total is counted per node, so absolute savings scale with fleet size.

Churn script: https://gist.github.com/zhaohuabing/33a57e7b6fd3126c17001c3208d288b2

Observability

New metrics reconcile_debounce_pending, reconcile_debounce_flush_total (labelled by flush reason) and reconcile_debounce_delay_seconds cover the debouncer, mirroring the watchable_debounce_* set in #9773.

The existing workqueue metrics already report what determines whether debouncing here helps at all: comparing the workqueue_adds_total rate against workqueue_work_duration_seconds shows whether the dirty-set dedup is already absorbing a burst. If adds/sec × work_duration is well above 1 the queue is coalescing on its own and there is little left to win; near 1 means roughly one reconcile per event.

Alternative

#9773 — debouncing the resource subscription instead with the same 100ms/10s defaults. That placement is downstream of the reconcile, so it does not avoid the provider reconcile, but it leaves status latency untouched.

The two share the debounce API and are alternatives rather than a stack — enabling both would compound the delay.

@zhaohuabing
zhaohuabing requested a review from a team as a code owner August 20, 2026 01:56
@netlify

netlify Bot commented Aug 20, 2026

Copy link
Copy Markdown

Deploy Preview for cerulean-figolla-1f9435 ready!

Name Link
🔨 Latest commit 448d26b
🔍 Latest deploy log https://app.netlify.com/projects/cerulean-figolla-1f9435/deploys/6a86641bcab86500080efec2
😎 Deploy Preview https://deploy-preview-9803--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.

@zhaohuabing
zhaohuabing marked this pull request as draft August 20, 2026 01:56
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.83193% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.36%. Comparing base (e5ba4ba) to head (448d26b).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
internal/provider/kubernetes/debounce.go 80.82% 9 Missing and 5 partials ⚠️
internal/provider/kubernetes/controller.go 45.45% 5 Missing and 1 partial ⚠️
api/v1alpha1/envoygateway_helpers.go 91.66% 1 Missing and 1 partial ⚠️
api/v1alpha1/validation/envoygateway_validate.go 81.81% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9803      +/-   ##
==========================================
+ Coverage   76.26%   76.36%   +0.09%     
==========================================
  Files         261      262       +1     
  Lines       44351    44557     +206     
==========================================
+ Hits        33826    34025     +199     
- Misses       8284     8285       +1     
- Partials     2241     2247       +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@zhaohuabing

Copy link
Copy Markdown
Member Author

@codex review

@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: 7b754de0d7

ℹ️ 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".

Comment thread api/v1alpha1/envoygateway_types.go
Every watch in the gatewayapi controller enqueues the same GatewayClass
request, so the workqueue already collapses events that arrive while a
reconcile is in flight: the request sits in the dirty set and is requeued
exactly once when Done is called. What the workqueue does not do is wait.
When a reconcile is fast relative to the event rate the queue drains
between events, and each event then costs a full rebuild of the resource
tree even though only the resulting state matters.

Wrap the controller's workqueue so that Add is held until no new request
has arrived for debounce.after, bounded by debounce.max so that sustained
churn cannot defer a reconcile indefinitely. AddAfter and AddRateLimited
reach the embedded queue directly, so error backoff is never delayed.

Reuses the same top-level debounce field in the EnvoyGateway config, and
is disabled by default. Note that status is computed during reconcile, so
enabling this also delays status updates by up to debounce.max.

Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com>
Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
@zhaohuabing
zhaohuabing force-pushed the provider-reconcile-debounce branch from 7b754de to 448d26b Compare August 20, 2026 02:19
@zhaohuabing
zhaohuabing marked this pull request as ready for review August 20, 2026 02:34
@zhaohuabing zhaohuabing changed the title feat: opt-in debounce for the provider reconcile performance: opt-in debounce for the provider reconcile Aug 20, 2026
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