Skip to content

fix: use inactivity timeout for watch instead of whole-request timeout - #3022

Open
duizabojul wants to merge 1 commit into
kubernetes-client:mainfrom
duizabojul:fix/watch-inactivity-timeout
Open

fix: use inactivity timeout for watch instead of whole-request timeout#3022
duizabojul wants to merge 1 commit into
kubernetes-client:mainfrom
duizabojul:fix/watch-inactivity-timeout

Conversation

@duizabojul

@duizabojul duizabojul commented Aug 24, 2026

Copy link
Copy Markdown

Fixes #3020

What / why

Watch passes AbortSignal.timeout(requestTimeoutMs) to the fetch that streams the watch body, so the signal fires 30s after the request starts regardless of connection health, and every watch — including one actively receiving events — is aborted with a TimeoutError.

Per the discussion in #3020, this PR keeps timeout signaling but changes what it measures:

  • src/watch.ts — replaces the whole-request AbortSignal.timeout with a resettable inactivity timer. The timer is armed before the fetch (connect timeout, same as today), then re-armed on the 200 response and on every body data chunk. A watch that keeps receiving data is left alone; a connection that goes silent for requestTimeoutMs (or never connects) is still aborted with the same TimeoutError as before. The timer is unref()ed to match AbortSignal.timeout's event-loop behavior, and is cleared once the watch finishes. No public API change; requestTimeoutMs keeps its name and 30s default, its meaning becomes "max time with no data received".
  • src/cache.tsListWatch now resets reconnectDelayMs in the existing TimeoutError branch of doneHandler. A client-side timeout means the client already waited the full requestTimeoutMs, so an immediate reconnect cannot tight-loop — the timeout itself is the throttle. Without this, a watch on a quiet resource (which now times out every requestTimeoutMs by design) accumulates exponential backoff and ends up alternating ~30s watching / ~30s not watching. Backoff is unchanged for real errors and server-side disconnects.

Tests

  • watch: a stream that keeps sending events outlives the timeout (lives ~2.4× requestTimeoutMs, then clean server close → done(null))
  • watch: a connection that goes idle after connecting (one event, then silence) still ends with TimeoutError
  • watch: the pre-existing connect-timeout test passes unchanged
  • cache: repeated TimeoutError reconnects apply no delay, while a subsequent non-timeout reconnect still backs off (via the injectable delayFn)

Both new watch tests and the cache test fail against the previous implementation.

npm test (356/356), npm run lint, npm run build all pass.

@kubernetes-prow kubernetes-prow Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 24, 2026
@kubernetes-prow

Copy link
Copy Markdown
Contributor

Welcome @duizabojul!

It looks like this is your first PR to kubernetes-client/javascript 🎉. Please refer to our pull request process documentation to help your PR have a smooth ride to approval.

You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. Here is the bot commands documentation.

You can also check if kubernetes-client/javascript has its own contribution guidelines.

You may want to refer to our testing guide if you run into trouble with your tests not passing.

If you are having difficulty getting your pull request seen, please follow the recommended escalation practices. Also, for tips and tricks in the contribution process you may want to read the Kubernetes contributor cheat sheet. We want to make sure your contribution gets all the attention it needs!

Thank you, and welcome to Kubernetes. 😃

@kubernetes-prow kubernetes-prow Bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Aug 24, 2026
@linux-foundation-easycla

linux-foundation-easycla Bot commented Aug 24, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: duizabojul / name: diouze (6086604)

@kubernetes-prow kubernetes-prow Bot added the cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. label Aug 24, 2026
@kubernetes-prow

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: duizabojul
Once this PR has been reviewed and has the lgtm label, please assign brendandburns for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@duizabojul
duizabojul force-pushed the fix/watch-inactivity-timeout branch from 2fdfe13 to b8a873e Compare August 24, 2026 14:17
Watch.watch() passed AbortSignal.timeout(requestTimeoutMs) to fetch, which
counts from request start and stays armed while the response body streams. Any
watch, healthy or not, was therefore aborted with a TimeoutError after 30s.

Replace it with a resettable timer on the AbortController: it is armed before
the fetch (connect timeout), then re-armed on the 200 response and on every
body chunk, so requestTimeoutMs now means "max time without data". The timer is
unref'd and cleared when the watch is done. No public API change.

Also reset ListWatch's reconnectDelayMs to 0 in the TimeoutError branch of
doneHandler. A client-side timeout means we already waited the full request
timeout, so reconnecting immediately cannot tight-loop, while backing off would
leave quiet resources unwatched for up to MAX_RECONNECT_DELAY_MS. Exponential
backoff still applies to real errors and server-side disconnects.
@duizabojul
duizabojul force-pushed the fix/watch-inactivity-timeout branch from b8a873e to 6086604 Compare August 24, 2026 14:25
@duizabojul

Copy link
Copy Markdown
Author

/easycla

@kubernetes-prow kubernetes-prow Bot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. and removed cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. labels Aug 24, 2026
@duizabojul
duizabojul marked this pull request as ready for review August 24, 2026 14:30
@kubernetes-prow kubernetes-prow Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 24, 2026
@davidgamero

Copy link
Copy Markdown
Contributor

this looks good to me. this exposed a couple other issues to address with watches like lack of jitter on the reconnects which i'll handle in a separate PR.
/lgtm

@kubernetes-prow kubernetes-prow Bot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Watch aborts healthy connections after 30s (2.0.0)

2 participants