reflector: back off when watch stream closes early to prevent reconnect storms - #920
Open
kaddynator wants to merge 3 commits into
Open
reflector: back off when watch stream closes early to prevent reconnect storms#920kaddynator wants to merge 3 commits into
kaddynator wants to merge 3 commits into
Conversation
…ct storms When the Kubernetes API server closes a watch stream shortly after the initial list (typically 2-6 seconds, seen with GKE and API gateways), the reflector would reconnect immediately and loop in a tight reconnect cycle. With timeout_seconds=10 this can produce dozens of reconnects per minute, degrading API server performance and triggering rate limits. Detect early stream close (watch_duration < 30s on a clean exit) and apply exponential backoff before reconnecting (doubles each cycle, capped at 30s). Watches that run >=30s reconnect immediately as before. A separate early_close_delay counter is used so this backoff does not interfere with the existing exception backoff tracked by cur_delay. Closes jupyterhub#577, related to jupyterhub#436
for more information, see https://pre-commit.ci
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
When the Kubernetes API server closes a watch stream shortly after the initial list — typically 2–6 seconds, observed on GKE and behind API gateways — the reflector reconnects immediately and enters a tight loop. With the default
timeout_seconds=10this produces dozens of reconnects per minute, degrading API server performance and triggering rate limiting (related to #436).This is the companion fix suggested in the inline review of #755, where @juliantaylor noted that "other k8s components typically relist watches in the range of minutes to never, every 10 seconds is a bit much" and @yuvipanda agreed it should be done in a separate PR.
Closes #577, related to #436, follow-on from #755
Reconnect behaviour: before and after
flowchart TD A([watch stream exits cleanly]) --> B{watch_duration ≥ 30s?} B -- Yes normal timeout or restart --> C[reset early_close_delay reconnect immediately] B -- No server closed early --> D[log warning sleep early_close_delay double delay ← capped at 30s] C --> E([start next watch]) D --> EBefore this change the
elsebranch always reconnected immediately, taking the left path for every stream exit — early or normal.Change
In the
elsebranch of_watch_and_update, detect whenwatch_duration < 30son a clean (non-exception) stream exit and apply exponential backoff before reconnecting. The delay doubles on each consecutive early close (starting at 0.1s, capped at 30s). Watches that run ≥30s — normaltimeout_secondsorrestart_secondsexpiry — reconnect immediately as before.A separate
early_close_delaycounter is used so this backoff does not interfere with the existing exception backoff tracked bycur_delay.No breaking changes
Deployments on clusters that close watch streams normally (at or after
timeout_seconds) will see no behavioral difference. The backoff only activates on abnormally short stream lifetimes.