Skip to content

fix(ssr): abort stuck component and in-flight fetch() on hydrate timeout - #6881

Merged
johnjenkins merged 6 commits into
mainfrom
fix-ssr-better-global-cleanup-plus-fetch-abort
Sep 11, 2026
Merged

fix(ssr): abort stuck component and in-flight fetch() on hydrate timeout#6881
johnjenkins merged 6 commits into
mainfrom
fix-ssr-better-global-cleanup-plus-fetch-abort

Conversation

@johnjenkins

@johnjenkins johnjenkins commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

What is the current behavior?

GitHub Issue Number: #6864

When renderToString / hydrateDocument hits opts.timeout the mock window is destroyed while components can still be mid-await (e.g. componentOnReady() > a component doing a fetch() call). Those components then resume against a torn-down window and throw, and the pending promises keep the render's object graph alive - under concurrent load this compounds into unbounded memory growth.

What is the new behavior?

Makes timeout actually cancels outstanding work instead of just tearing down around it:

  • Stencil's own post-render bookkeeping stops touching the window once time's up
  • in-flight fetch() calls a component made are aborted rather than left to run to completion (via shimming fetch to add an AbortController (< whilst still respecting incoming AbortControllers))
  • AbortController itself is also shimmed, so any AbortController a component creates for its own cancellable work (participating libs include Axios, AWS SDK, the MongoDB Node driver, etc.) is automatically aborted at timeout
  • Anything that manages to touch the destroyed window afterward fails with a clear error

Fixes #6864

Limitation: for a non-cancellable, truly hung promise, there's nothing Stencil can do - no in-language way exists to force-settle a promise nobody is ever going to resolve, so the object graph it's holding reachable can't be released either.

Documentation

Does this introduce a breaking change?

  • Yes
  • No

Testing

Other information

Why not the ceiling / drain approach?

#6865 (the issue raiser preferred solution) waits for pending components to settle before destroying the window, bounded by a second timeout (the same length as the original). To my mind, that's just a double timeout by a different name and kicks the timeout bomb down the road.

It only helps components that finish a bit late (and if that's the case just adjust opts.timeout). A genuinely stuck component (probably a more likely scenario) hits the identical crash, just now by double the timeout period.
Additionally, doubling how long each timed-out render holds resources before releasing makes any memory pressure worse, not better.

@johnjenkins
johnjenkins requested a review from a team as a code owner September 11, 2026 00:00
@johnjenkins
johnjenkins merged commit cf5982e into main Sep 11, 2026
31 checks passed
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.

Hydrate timeout resets the mock window while component promises are still pending

1 participant