Skip to content

feat(ssr): coordinate remote cache invalidation and Modern updates - #5044

Draft
2heal1 wants to merge 17 commits into
mainfrom
feat/mf-ssr-clear-cache
Draft

2heal1 wants to merge 17 commits into
mainfrom
feat/mf-ssr-clear-cache

Conversation

@2heal1

@2heal1 2heal1 commented Sep 9, 2026 •

Copy link
Copy Markdown
Member

Description

Updating an SSR remote must invalidate both its MF state and the host modules that retain its exports. This PR adds explicit remote replacement and connects cache invalidation to Modern's SSR lifecycle: statically owned dependencies use selective entry updates; dynamic or unprovable dependencies rebuild the SSR application in the same process. Incoming requests wait within the affected scope, and new SSR responses and hydration use the selected release.

Cross-repository implementation

Repository / PR Responsibility
MF — #5044 (this PR) Add explicit updateRemotes, deprecate force registration, and coordinate remote/container cleanup with bound bundler runtimes. Invalidate affected host consumers while preserving live shared dependency closures; dispose obsolete runtime bindings. The Modern adapter maps compiled ownership to SSR entries, handles serialized revisions and batch updates, selects selective/full publication, and supplies release information for hydration.
Modern — #8865, building on #8861 and #8862 Provide bounded request admission, drain active requests and unfinished producer work, and publish either selected SSR entries or rebuilt application resources with refreshed cache namespaces. Keep the HTTP listener and PID alive; failed publication stays unavailable until explicit recovery. These PRs are merged into feat/mf-ssr-clear-cache; a branch-to-main umbrella PR was not found at the time of this update.
Rspack — #14368 Supply generic MF dependency metadata for remote modules, host consumers/importers and chunks, plus Node chunk-cache controls and generation checks to prevent stale asynchronous loads from restoring invalidated state. Expose provider cache-cleaning hooks. Framework-specific entry ownership and update policy remain in the Modern JS plugin.

Observable behavior

Updating Remote A reinitializes its affected host entry. With selective updates, an independent B entry keeps its module identity and continues serving requests; with application-wide rebuilding, B is also reinitialized and waits during publication. Neither path restarts the Node process. Partial scope is expanded when entries share a runtime, and incomplete dependency evidence falls back to application rebuilding.

SSR application rebuilding is runtime resource loading, not a fresh Rspack compilation. It does not automatically undo arbitrary business globals, listeners or background tasks.

Validation and remaining integration work

Recorded validation covers runtime/bundler cache cleanup, shared identity retention, stale loads, actual compiled selective/full SSR updates, revisions and batch publication, hydration, bounded queues/timeouts, and repeated-update memory sampling. The latest independent browser E2E also verifies B's retained/recreated module identity and unchanged PID. Exact commands and validation boundaries are recorded in VALIDATION.md.

The acceptance setup pins MF 0.0.0-feat-mf-ssr-clear-cache-20260914081229, Modern 0.0.0-canary-20260914082926 and Rspack 2.2.3-canary-fde17bab-20260911103204. It still applies the Modern duplicate-pipe fix from #8872 as a pnpm patch, and disables server splitChunks in the MPA fixtures because the tested shared-entry output failed initialization. The new sibling demo has an independent E2E entry and has not replaced the existing SSR CI entry. These are retained integration limits, not claims of completed release readiness; full current-head validation remains required before merging to main.

Demo guide (中文)

Related Issue

Cross-repository implementation PRs are linked above. No separate issue is linked.

Types of changes

  • Docs change / refactoring / dependency upgrade
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Registration compatibility note: duplicate registration no longer serves as an update path; callers should migrate to updateRemotes. The branch includes a breaking-change commit for this API transition.

Checklist

  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • I have updated the documentation.

The full-suite checkbox remains unchecked pending current-head integration validation; recorded passing checks are described above.

@changeset-bot

changeset-bot Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b64085e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 48 packages
Name Type
@module-federation/runtime-core Major
@module-federation/webpack-bundler-runtime Major
@module-federation/runtime Major
@module-federation/modern-js-v3 Major
@module-federation/sdk Major
@module-federation/nextjs-mf Patch
@module-federation/bridge-react Major
@module-federation/enhanced Major
@module-federation/esbuild Patch
@module-federation/runtime-tools Major
@module-federation/devtools Major
@module-federation/dts-plugin Major
@module-federation/metro Major
@module-federation/modern-js Major
@module-federation/node Patch
@module-federation/observability-plugin Patch
@module-federation/playground Patch
@module-federation/retry-plugin Major
@module-federation/bridge-vue3 Major
website-new Patch
shared-tree-shaking-with-server-host Patch
shared-tree-shaking-with-server-provider Patch
@module-federation/cli Major
@module-federation/managers Major
@module-federation/manifest Major
@module-federation/rsbuild-plugin Major
@module-federation/rspack Major
@module-federation/rspress-plugin Major
@module-federation/rstest Major
@module-federation/storybook-addon Patch
@module-federation/utilities Patch
@module-federation/bridge-react-webpack-plugin Major
shared-tree-shaking-no-server-host Patch
shared-tree-shaking-no-server-provider Patch
@module-federation/inject-external-runtime-core-plugin Major
@module-federation/metro-plugin-rnc-cli Major
@module-federation/metro-plugin-rnef Major
@module-federation/metro-plugin-rock Major
node-dynamic-remote-new-version Patch
node-dynamic-remote Patch
remote5 Patch
remote6 Patch
create-module-federation Major
@module-federation/error-codes Major
@module-federation/third-party-dts-extractor Major
@module-federation/treeshake-frontend Major
@module-federation/treeshake-server Major
@module-federation/bridge-shared Major

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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-09T11:18:40.114198Z 7d503c1 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pkg-pr-new

pkg-pr-new Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@module-federation/devtools

pnpm add https://pkg.pr.new/@module-federation/devtools@ca291d6

@module-federation/cli

pnpm add https://pkg.pr.new/@module-federation/cli@ca291d6

create-module-federation

pnpm add https://pkg.pr.new/create-module-federation@ca291d6

@module-federation/dts-plugin

pnpm add https://pkg.pr.new/@module-federation/dts-plugin@ca291d6

@module-federation/enhanced

pnpm add https://pkg.pr.new/@module-federation/enhanced@ca291d6

@module-federation/error-codes

pnpm add https://pkg.pr.new/@module-federation/error-codes@ca291d6

@module-federation/esbuild

pnpm add https://pkg.pr.new/@module-federation/esbuild@ca291d6

@module-federation/managers

pnpm add https://pkg.pr.new/@module-federation/managers@ca291d6

@module-federation/manifest

pnpm add https://pkg.pr.new/@module-federation/manifest@ca291d6

@module-federation/metro

pnpm add https://pkg.pr.new/@module-federation/metro@ca291d6

@module-federation/metro-plugin-rnc-cli

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rnc-cli@ca291d6

@module-federation/metro-plugin-rnef

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rnef@ca291d6

@module-federation/metro-plugin-rock

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rock@ca291d6

@module-federation/modern-js

pnpm add https://pkg.pr.new/@module-federation/modern-js@ca291d6

@module-federation/modern-js-v3

pnpm add https://pkg.pr.new/@module-federation/modern-js-v3@ca291d6

@module-federation/native-federation-tests

pnpm add https://pkg.pr.new/@module-federation/native-federation-tests@ca291d6

@module-federation/native-federation-typescript

pnpm add https://pkg.pr.new/@module-federation/native-federation-typescript@ca291d6

@module-federation/nextjs-mf

pnpm add https://pkg.pr.new/@module-federation/nextjs-mf@ca291d6

@module-federation/node

pnpm add https://pkg.pr.new/@module-federation/node@ca291d6

@module-federation/observability-plugin

pnpm add https://pkg.pr.new/@module-federation/observability-plugin@ca291d6

@module-federation/playground

pnpm add https://pkg.pr.new/@module-federation/playground@ca291d6

@module-federation/retry-plugin

pnpm add https://pkg.pr.new/@module-federation/retry-plugin@ca291d6

@module-federation/rsbuild-plugin

pnpm add https://pkg.pr.new/@module-federation/rsbuild-plugin@ca291d6

@module-federation/rspack

pnpm add https://pkg.pr.new/@module-federation/rspack@ca291d6

@module-federation/rspress-plugin

pnpm add https://pkg.pr.new/@module-federation/rspress-plugin@ca291d6

@module-federation/rstest

pnpm add https://pkg.pr.new/@module-federation/rstest@ca291d6

@module-federation/runtime

pnpm add https://pkg.pr.new/@module-federation/runtime@ca291d6

@module-federation/runtime-core

pnpm add https://pkg.pr.new/@module-federation/runtime-core@ca291d6

@module-federation/runtime-tools

pnpm add https://pkg.pr.new/@module-federation/runtime-tools@ca291d6

@module-federation/sdk

pnpm add https://pkg.pr.new/@module-federation/sdk@ca291d6

@module-federation/storybook-addon

pnpm add https://pkg.pr.new/@module-federation/storybook-addon@ca291d6

@module-federation/third-party-dts-extractor

pnpm add https://pkg.pr.new/@module-federation/third-party-dts-extractor@ca291d6

@module-federation/treeshake-frontend

pnpm add https://pkg.pr.new/@module-federation/treeshake-frontend@ca291d6

@module-federation/treeshake-server

pnpm add https://pkg.pr.new/@module-federation/treeshake-server@ca291d6

@module-federation/typescript

pnpm add https://pkg.pr.new/@module-federation/typescript@ca291d6

@module-federation/utilities

pnpm add https://pkg.pr.new/@module-federation/utilities@ca291d6

@module-federation/webpack-bundler-runtime

pnpm add https://pkg.pr.new/@module-federation/webpack-bundler-runtime@ca291d6

@module-federation/bridge-react

pnpm add https://pkg.pr.new/@module-federation/bridge-react@ca291d6

@module-federation/bridge-react-webpack-plugin

pnpm add https://pkg.pr.new/@module-federation/bridge-react-webpack-plugin@ca291d6

@module-federation/bridge-shared

pnpm add https://pkg.pr.new/@module-federation/bridge-shared@ca291d6

@module-federation/bridge-vue3

pnpm add https://pkg.pr.new/@module-federation/bridge-vue3@ca291d6

@module-federation/inject-external-runtime-core-plugin

pnpm add https://pkg.pr.new/@module-federation/inject-external-runtime-core-plugin@ca291d6

commit: ca291d6

@github-actions

github-actions Bot commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

Bundle Size Report

19 package(s) changed, 24 unchanged.

Package dist + ESM entry

Package Total dist (raw) Delta ESM gzip Delta
@module-federation/bridge-react 308.7 kB +27 B (+0.0%) 1.5 kB no change
@module-federation/bridge-vue3 198.5 kB +6.8 kB (+3.6%) 28.4 kB +1.1 kB (+3.8%)
@module-federation/dts-plugin 336.2 kB +799 B (+0.2%) 4.7 kB no change
@module-federation/enhanced 817.7 kB +476 B (+0.1%) 672 B no change
@module-federation/managers 70.7 kB no change 243 B -1 B (-0.4%)
@module-federation/modern-js 201.0 kB no change 1.2 kB +1 B (+0.1%)
@module-federation/modern-js-v3 335.1 kB +140.9 kB (+72.6%) 876 B no change
@module-federation/playground 29.03 MB +124.2 kB (+0.4%) 45.6 kB -76 B (-0.2%)
@module-federation/runtime 21.8 kB +1.4 kB (+7.0%) 750 B +26 B (+3.6%)
@module-federation/runtime-core 321.1 kB +10.6 kB (+3.4%) 570 B no change
@module-federation/sdk 129.1 kB +180 B (+0.1%) 785 B no change
@module-federation/treeshake-frontend 626.0 kB -535 B (-0.1%) 0 B no change
@module-federation/treeshake-server 773.7 kB -535 B (-0.1%) 124 B no change
@module-federation/webpack-bundler-runtime 133.6 kB +65.5 kB (+96.2%) 569 B +38 B (+7.2%)

Bundle targets

Package Web bundle (gzip) Delta Node bundle (gzip) Delta
@module-federation/bridge-vue3 20.8 kB +826 B (+4.0%) 21.0 kB +817 B (+4.0%)
@module-federation/cli 2.3 kB -11 B (-0.5%) 2.4 kB -34 B (-1.4%)
@module-federation/core 1.0 kB -4 B (-0.4%) 1.0 kB -34 B (-3.1%)
@module-federation/devtools 30.3 kB -6 B (-0.0%) 30.3 kB -31 B (-0.1%)
@module-federation/enhanced 2.7 kB +1 B (+0.0%) 2.8 kB -45 B (-1.6%)
@module-federation/metro-plugin-rnc-cli 416 B -1 B (-0.2%) 435 B -26 B (-5.6%)
@module-federation/modern-js 5.1 kB +1 B (+0.0%) 5.1 kB +1 B (+0.0%)
@module-federation/modern-js-v3 6.4 kB +1.4 kB (+28.8%) 6.4 kB +1.4 kB (+28.8%)
@module-federation/node 9.1 kB +1 B (+0.0%) 9.1 kB -30 B (-0.3%)
@module-federation/playground 41.4 kB -55 B (-0.1%) 41.4 kB -55 B (-0.1%)
@module-federation/runtime 733 B +36 B (+5.2%) 733 B +36 B (+5.2%)
@module-federation/runtime-core 16.9 kB +780 B (+4.7%) 16.6 kB +776 B (+4.8%)
@module-federation/sdk 4.5 kB +3 B (+0.1%) 5.9 kB +4 B (+0.1%)
@module-federation/webpack-bundler-runtime 8.1 kB +4.0 kB (+97.4%) 8.1 kB +4.0 kB (+97.4%)

Tree-shakable entrypoints

Package Export Entry gzip Delta Web bundle (gzip) Delta Node bundle (gzip) Delta Gap (node-web) Delta
@module-federation/runtime ./bundler 197 B +13 B (+7.1%) 733 B +36 B (+5.2%) 733 B +36 B (+5.2%) 0 B 0 B
@module-federation/webpack-bundler-runtime ./bundler 151 B no change 8.1 kB +4.0 kB (+97.4%) 8.1 kB +4.0 kB (+97.4%) 0 B 0 B

Consumer scenarios

Scenario Web output (gzip) Delta Node output (gzip) Delta Gap (node-web) Delta
Enhanced remoteEntry 27.0 kB +4.8 kB (+21.4%) 28.6 kB +4.7 kB (+19.8%) +1.5 kB -45 B

Total dist (raw): 36.50 MB (+349.9 kB (+0.9%))
Total ESM gzip: 112.5 kB (+1.0 kB (+0.9%))
Total web bundle (gzip): 260.0 kB (+7.0 kB (+2.7%))
Total node bundle (gzip): 262.2 kB (+6.8 kB (+2.6%))
Tracked ./bundler entry gzip: 576 B (+13 B (+2.3%))
Tracked ./bundler web bundle (gzip): 8.9 kB (+4.0 kB (+82.5%))
Tracked ./bundler node bundle (gzip): 8.9 kB (+4.0 kB (+82.5%))

Bundle sizes are generated with rslib (Rspack). Package-root metrics preserve the historical report. Tracked subpath exports such as ./bundler are measured separately so ENV_TARGET-driven tree-shaking is visible. Bare imports are externalized to keep package-level sizes consistent, and assets are emitted as resources.

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

ℹ️ 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 on lines +895 to +897
if (hasActiveSharedConsumers(target)) {
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Evict non-shared caches when retaining a shared provider

When the removed provider has a loaded shared dependency consumed by another runtime—a common case for React or antd—this return skips deletion of its remote-module factories, wrapper modules, chunks, and entry cache. Direct removeRemote subsequently cleans only the core module cache, while the forced-registration wrapper performs no later cleanup at all, so non-shared exposes can remain reachable or a forced replacement can continue serving the old implementation. Retain the active shared record without bypassing eviction of the provider's unrelated modules.

Useful? React with 👍 / 👎.

Comment on lines +318 to +320
'for(var moduleId in moduleCache) {',
Template.indent([
`${RuntimeGlobals.hasOwnProperty}(moduleCache, moduleId) && delete moduleCache[moduleId];`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid clearing the container's entire module cache

When a remote entry shares its webpack runtime with another entrypoint or with its own running application, invoking __webpack_clear_cache__ deletes every initialized module in that compilation, not just modules belonging to the removed exposes. Unrelated stateful modules will therefore be re-executed on their next require, potentially duplicating application initialization and singleton state; the hook needs to target only modules owned by the container/exposes.

Useful? React with 👍 / 👎.

Comment on lines +1310 to +1313
if (!options?.force) {
return originalRegisterRemotes(remotes as any, options as any);
}
return registerRemotesWithForce(webpackRequire, instance, remotes);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Emit removal hooks during forced registration

With the bundler runtime installed, every registerRemotes(..., { force: true }) call takes this replacement path instead of the core implementation, and registerRemotesWithForce never emits the newly added removeRemote lifecycle hook. Runtime plugins that release provider-specific resources in that hook work for explicit removal but are silently bypassed for forced replacement, despite the core path defining replacement as removal followed by registration.

Useful? React with 👍 / 👎.

Comment on lines +104 to +109
export function removeRemote(
...args: Parameters<ModuleFederation['removeRemote']>
): ReturnType<ModuleFederation['removeRemote']> {
assert(FederationInstance, RUNTIME_009, runtimeDescMap);
// eslint-disable-next-line prefer-spread
return FederationInstance.removeRemote.apply(FederationInstance, args);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add a changeset for the published runtime behavior

This commit adds the public removeRemote API and changes behavior in multiple publishable packages, but the commit contains no .changeset file, so the release plan will not version or publish these changes. Add a changeset covering the affected packages as required by the repository release policy.

AGENTS.md reference: AGENTS.md:L221-L227

Useful? React with 👍 / 👎.

@2heal1
2heal1 marked this pull request as draft September 10, 2026 03:08
Comment thread apps/modernjs-ssr/cache-updates/e2e/production.cjs Fixed
function loadFrames() {
const url = state.hosts[selected].url;
$('old').src = url + '/';
$('new').src = url + $('entry').value;
frame = document.createElement('iframe');
frame.id = 'traffic-preview';
frame.title = '更新期间发起的真实 SSR 页面请求';
frame.src = host.url + visit.entry + '?id=iframe-' + exp.started;
});
$('reload').onclick = () => {
$('new').src =
state.hosts[selected].url + $('entry').value + '?t=' + Date.now();
res.end('Control API only');
} catch (e) {
res.writeHead(500);
res.end(JSON.stringify({ error: String(e) }));
res.end('Control API only');
} catch (e) {
res.writeHead(500);
res.end(JSON.stringify({ error: String(e) }));
return Response.json(result);
} catch (e) {
console.error(e);
return new Response(String(e.stack), { status: 500 });
res.end(await fs.readFile(path.join(dir, file)));
} catch (e) {
res.writeHead(500);
res.end(JSON.stringify({ error: String(e) }));
res.end(await fs.readFile(path.join(dir, file)));
} catch (e) {
res.writeHead(500);
res.end(JSON.stringify({ error: String(e) }));
@2heal1 2heal1 changed the title feat: clear SSR remote cache on removal feat(ssr): coordinate remote cache invalidation and Modern updates Sep 15, 2026

This branch was successfully deployed

1 active (outdated) deployment
npm — ca291d6c Deployed Sep 14, 2026 by 2heal1 via Release next from feat/mf-ssr-clear-cache #5713
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants