fix(runtime-core): clear rejected remote-entry promises from globalLoading - #5006
Conversation
…ading Rejected getRemoteEntry loads left their promise in globalLoading forever, so later callers reused the failed thenable unless retry-plugin deleted the key. Clear the cache entry on terminal rejection while keeping successful loads cached, and use an identity check so an older rejection cannot delete a newer in-flight request.
🦋 Changeset detectedLatest commit: 7299797 The changes in this PR will be included in the next version bump. This PR includes changesets to release 48 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 21ff2e8735
ℹ️ 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".
| loading.then(undefined, () => { | ||
| if (globalLoading[uniqueKey] === loading) { | ||
| delete globalLoading[uniqueKey]; |
There was a problem hiding this comment.
Add a patch changeset for the runtime behavior change
This changes the published behavior of @module-federation/runtime-core, but the commit contains no .changeset/*.md file. Consequently, the release plan will not record or version this fix, so consumers may never receive it through the normal release process; add a patch changeset covering @module-federation/runtime-core and its fixed package group.
AGENTS.md reference: AGENTS.md:L221-L227
Useful? React with 👍 / 👎.
The rejected-promise cleanup changes published runtime-core behavior and needs a changeset so the fix ships in a release.
Problem
getRemoteEntrystores the in-flight load promise inglobalLoading[uniqueKey]. On terminal rejection the.catchrethrows without deleting the cache entry, so later callers keep receiving the same rejected promise.Retry-plugin only deletes the key in
beforeExecuteRetry. Hosts without retry-plugin (or after exhausted retries) still poison the shared cache. Successful loads must remain cached.Root cause
In
packages/runtime-core/src/utils/load.ts, after assigning the loading promise toglobalLoading[uniqueKey], a rejected promise stayed in the cache indefinitely. There was no cleanup on terminal failure at the runtime-core layer.Why fix this in runtime-core (not only retry-plugin)
getRemoteEntry: Own theglobalLoadingcache lifecycle — share in-flight work, keep successes, clear terminal failures so a later call can start a new load.Putting cleanup in runtime-core fixes all consumers; retry-plugin remains complementary for active retry orchestration.
Behavior after this change
globalLoading(unchanged).globalLoading[uniqueKey] === loading) so an older rejection cannot delete a newer deferred request.No unbounded auto-retries are introduced. The original loading promise identity remains in the cache (cleanup is a side-effect thenable, not a replacement).
Related (not duplicated)
Test plan