9

I've been playing around with this module federation example, where the setup is relatively straightforward - a host is consuming a module from a remote with a shared react dependency. When running this locally, I noticed that despite both host and remote having the same react/react-dom versions, the remote's version is always the one downloaded.

Based on my research it seems that module federation will pick the "best" version of shared dependencies, but I'm surprised that the remote one will be chosen in a case where both have the same version. How is this decision made? Is there a way to force the host's version to be used in this case?

Niko Savas
  • 93
  • 1
  • 5
  • Did you ever figure this out? Our team is encountering the exact same issue (both host and remote use same version, but remote gets downloaded). – Derek Greer May 02 '22 at 16:10
  • I had the exact same issue (both host and remote use same version, but remote gets downloaded) – ZhaoWei Jul 11 '22 at 07:04

2 Answers2

7

Basically, when your host starts up it will register all the versions it has into the shared scope. Every time you load a remoteEntry.js from a remote, the remote will also add their versions to this same scope, but only if that exact version does not exist already.

So for example, if the host shares module-a at version 1.0.0. When the host loads it will put module-a:1.0.0 into the shared context. If the remote also shares module-a:1.0.0 it will not put it in the context, because it is already there. If the host was sharing module-a:1.0.1 then the context will now have two versions: the module-a:1.0.0 from the host and module-a:1.0.1 from the remote.

At this point we are just talking about registration... we haven't chosen which version to use, but we are registering all unique versions shared from all remotes and hosts. And basically the first to register it wins.

Now when the version resolution algorithm runs... it will figure out based on all the requirements which version to use. If the algorithm chooses version 1.0.0 of the module, then it will go to the scope and use whatever module is assigned to version 1.0.0 which in this case would be the one from the host, because the host ran first and was able to register it first. If the algorithm picked 1.0.1 it would use the module from the remote. If multiple remotes provided 1.0.1 then it will use the one from the remote that first registered it into the scope.

Daniel Tabuenca
  • 13,147
  • 3
  • 35
  • 38
  • 3
    So, Niko says: "I'm using the same version for both host and remote, but remote gets used". Daniel says: "When both host and remote use the same version, remote will get used because it gets loaded first.". So, Daniel says what Niko says is happening wouldn't happen. For my team, which is seeing the same behavior as Niko, this answer doesn't help. – Derek Greer May 02 '22 at 16:08
  • It may not be deterministic which one gets used, but it shouldn't matter if they are both the same exact versions. The one that will get used is whichever remoteEntry.js script got loaded first. Since things can load in parallel – Daniel Tabuenca May 03 '22 at 18:16
  • @DanielTabuenca does that mean shared deps. will always be downloaded no matter if it already exists(same version) with host ? – Rohit Garg Jul 08 '22 at 13:09
  • 2
    Had some extra time to look into this today - it looks like part of this answer is incorrect - If you look [here](https://github.com/webpack/webpack/blob/0c3df8ea25087effbf03e21a13363acac82c7b96/lib/sharing/ShareRuntimeModule.js#L90), nstead of being "first to register wins", it is actually the "greatest app name by string comparison" that wins. If you change the name of `app1` to `app3`, then its version will deterministically be used because `"app3" > "app2"` but if you leave it as-is in the example, then `app2` will be used because `"app2" > "app1"` – Niko Savas Jul 12 '22 at 16:31
  • 1
    @RohitGarg no the only thing that is initially loaded from all the remotes is the remoteEntry.js which only has a manifest not the actual sripts. Dependencies will get loaded from individual remotes once federation determines where to get it from. It's important to split dependencies right though, otherwise there is a chance of loading something twice since the dependency may exist within a larger bundle. – Daniel Tabuenca Jul 26 '22 at 16:46
  • In an MFE architecture using module federation, what happens when you need to update a major version of a package that needs to be a singleton, like `react` and `react-dom`? Of course, the MFE won't be able to do it at the same time. – cbdeveloper Feb 08 '23 at 10:20
4

This article explains the mechanism very well. https://www.angulararchitects.io/en/aktuelles/getting-out-of-version-mismatch-hell-with-module-federation/

Xiaofeng Xie
  • 145
  • 9
  • That's a great resource! I don't think it explains my question specifically, though. In an example where the host and remote had the same version of a dependency, why does it choose to use the remote version instead of the host version? – Niko Savas Feb 09 '21 at 20:04
  • IT choses to use whichever one managed to register first. The registrations happen through the included remoteEntry.js files during an asynchronous step in which they may all load in parallel. From the point of view of the browser, they are all remote. – Daniel Tabuenca May 03 '22 at 18:17