Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ChunkID collisions when using Federated Module Plugin #5964

Open
jdthorpe opened this issue Feb 12, 2022 · 9 comments
Open

ChunkID collisions when using Federated Module Plugin #5964

jdthorpe opened this issue Feb 12, 2022 · 9 comments

Comments

@jdthorpe
Copy link

jdthorpe commented Feb 12, 2022

Bug report

What is the current behavior?

I am trying to import two modules using the federated module plugin with a setup like this:

new ModuleFederationPlugin({
    name: "module-a",
    filename: "remoteEntry.js",
    library: { type: "var", name: "module_a" },
    exposes: {
        "./lib": "./bootstrap",
    },
})

and a nearly identical setup for module-b.

However, since both of the federated modules use the same file name at their root module (e.g. bootstrap.ts), both get assigned the same chunkId ("626", which is just the hash of './node_modules/ts-loader/index.js!./bootstrap.ts' using the numberHash function)

In a normal webpack build, the file paths for each chunk would be unique, but since I have multiple federated modules that expose a ./bootstrap.ts, each get the same chunk id (656, in this case). This causes an id collision in the runtime, as both federated modules request the same chunkid, and as result there is a race condition, wherein the first chunk with the id 626 to be requested gets by both federated modules.

I tried setting outputs.chunkFilename: "module-a-[id].js" (and likewise for module-b) but this doesn't seems to change the chunkId, and in the network activity, I can see that only one of the two chunks gets loaded and is imported (erroneously) into both federated modules.

Reading the code, it looks like the chunk IDs get set by the DeterministicChunkIdsPlugin, which doesn't expose a hashSalt option or make use of the options.hashSalt, so there doesn't seem to be a way change the hash to prevent id collisions.

Is there any way to prevent this kind of id collisions with federated modules?

If the current behavior is a bug, please provide the steps to reproduce.

This repo illustrates the race condition. In the example, there are two nearly identical federated modules that expose a src/index.js file which are simple enough:

// module_a/src/index.js
export const message = "Hi from Module A"

and

// module_b/src/index.js
export const message = "Hi from Module B"

The host app simply imports these and displays the results with:

function App() {
    const [a, set_a] = useState()
    const [b, set_b] = useState()

    useEffect(() => {
        import(`module_a/Message`).then((m) => { set_a(m.message) })
        import(`module_b/Message`).then((m) => { set_b(m.message) })
    }, [])

    return (
        <>
            <p>Module A says: {a}</p>
            <p>Module B says: {b}</p>
        </>
    )
}

When the app is run, the messages read either

Module A says: Hi from Module A
Module B says: Hi from Module A

or:

Module A says: Hi from Module B
Module B says: Hi from Module B

Depending on whether module_a/remoteEntry.js or module_b/remoteEntry.js finishes loading first. (The Nginx server in the repo introduces a random delay to help make the switching behavior more obvious)

What is the expected behavior?

When two federated modules are loaded onto the page, the correct module is reliably loaded

Other relevant information:
webpack version: 5.68.0
Node.js version: 14.16.0
Operating System: Windows, Linux, OSX
Additional tools:

@jdthorpe
Copy link
Author

Thanks! That worked, but I don't see it mentioned in the official docs Is there another readme I should be reading?

@alexander-akait
Copy link
Member

We have this on another page, but yes make sense to improve this on this page too

@alexander-akait alexander-akait transferred this issue from webpack/webpack Feb 12, 2022
@c-falardeau
Copy link

I'm experiencing the same problem, but in my case, I have a shell that uses a the library A and I load multiple federated components that use also the library A (but they might use a different version).

In the shell and federated component, I've setup the module federation plugin to not share the library A.
It seems that the module id for the library A is the same for the shell and the federated components.
When the shell loads and calls a function in library A, it seems to refer to the wrong library A chunk (it was three shaked differently) and the method that I call is missing, but looking in the other chunks with same module id (loaded later in the network tab), the function is there.

I think this is a bug

Shell -> Library A
Federated Page -> Library A
Federated Component -> Library A

Library A is not shared cause it contains a context API that we won't share between MFEs

All instance of Library A have 805 as module id

When the Shell initializes and call a hook defined in the Library A, I get an error saying it does not exist, but it is there in another chunk with the same module id.

@alexander-akait
Copy link
Member

Check you don't have different version of Library-A

@c-falardeau
Copy link

c-falardeau commented Apr 17, 2023

the shell loads 2.9.1
the page loads 2.9.1
the component loads 2.9.0

This is the shell that fails to load

@alexander-akait
Copy link
Member

As you can see you have different versions, you can use version (as prefix or suffic) in name to prevent such cases

@flyinhigh
Copy link

we set splitChuks like this in module A and B ,the remoteEntry.js of them has the same chunk id of vendor, they influence each other.

optimization: {
runtimeChunk: false,
splitChunks: {
chunks: 'async',
cacheGroups: {
vendors: {
test: /[\/]node_modules[\/]/,
priority: -10,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
}

@AbhiPartha
Copy link

Give different names here https://github.com/jdthorpe/federated-module-race-condition/blob/main/module_a/package.json#L2 and here https://github.com/jdthorpe/federated-module-race-condition/blob/main/module_b/package.json#L2, we have this in readme

This creates a unique global name and thus the chunk id won't clash. You can also use the uniqueName under output attribute of webpack config if you think for some reason the package.json name isn't picked. The default value is an empty string and can cause the above issue.

In our case we fixed this issue by using uniqueName for Shell and advised our tenants to use a unique name preferably their own package.json name. The reason why the config didn't pick the package.json name in our case is not clear.

But it could be that our configs aren't at the root and we use monorepos which have many package.json

Adding for those folks for whom the unique package.json name still has issues.

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

No branches or pull requests

5 participants