-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
fix(hmr): implement better module node equality checks #12125
Conversation
it('should update existing values', () => { | ||
const set = new HashSet<{ | ||
id: string | ||
value: string | ||
}>({ | ||
getHash: (value) => value.id, | ||
}) | ||
|
||
set.add({ id: '1', value: 'a' }) | ||
set.add({ id: '1', value: 'b' }) | ||
|
||
expect(set.size).toBe(1) | ||
expect(set.has({ id: '1', value: 'a' })).toBe(true) | ||
expect(set.get({ id: '1', value: 'b' })?.value).toBe('b') | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: not sure if this is the behavior we want, let me know if we don't want to update entries with the same id
export const isModuleNodeEqual = | ||
(a: ModuleNode) => | ||
(b: ModuleNode): boolean => | ||
getModuleNodeHash(a) === getModuleNodeHash(b) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: there is probably a better way to do this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about this fix.
I think expecting reference equality for module in the graph is totally fine.
The set for boundaries is effectively is bad pattern. But it's only used as an append only data structure, so an array would be enough.
Do you have an clue where the reference equality fails and triggers an infinite recursion?
@ArnaudBarre we inline the boundary when adding it to the set here so the reference check will always fail vite/packages/vite/src/node/server/hmr.ts Lines 246 to 249 in 167753d
For background, this PR was created because the Maybe this isn't the root cause though edit: after rereading your question again, if you are asking about problem 2 and this unit test vite/packages/vite/src/node/server/__tests__/hmr.spec.ts Lines 7 to 22 in fd01991
I have not been able to reproduce the cycle and am totally fine with removing those changes from this pr |
An array here causes terrible HMR performance in some cases. The Set gets mapped to an array in For reference, I get HMR triggers that include only 14 files, but triggers 20k+ updates of those same 14 files (between 1 and 5k per file). HMR takes 30-60s w/ my browser basically frozen. This renders HMR basically unusable. I am using aliases to other modules in a monorepo, and when I remove those aliases and just reference the built files, I get 67k updates for changing the same line in the built version. I added some quick and dirty de-dupe to the Set after the I'm not sure why a |
If the nodes being passed in truly are stable, sure the |
There is something wrong in the boundaries propagation so. Even with a set for data structure, it stills means the propagate update is doing cycles without detecting circular references or something like this. We should try to understand what happens there and fix this, dedupe is not the root fix. As I said, the current use of Set is misleading, but we aren't doing any reference check with it, so we should simplify the data structure should just be an array (if we keep the current algorithm). First what is important is to understand what fails in the current algorithm, and then we can discuss how to solve the reference equality or avoid the mismatch of it. |
Makes sense to me |
@benatshippabo I think we should close this PR, no? As it was discussed, we should check why there are duplicated modules in the first place. The solution will probably not involve a HashSet. We could keep discussing in #12062, and this PR with the discussion until here is already linked to it as a reference. |
If this is closed, can #12062 at least be bumped up in priority? It is a horrible performance problem in normal HMR situations on a large project, not just when checking out a branch while Vite is running |
Not the solution we are looking for |
Description
Problem 1: using
set.has(moduleNode)
Currently, we use a
Set
to try to deduplicate objects inhmr.ts
. This will not work if the boundary is not referencing the same object..Map<ModuleNode, Set<ModuleNode>>
will not work for the same reasonSo we create a
HashSet
class which takes a hash function and replace usages ofSet
withHashSet
. The hash function we plan to use is as follows:Problem 2: using
moduleNodes.includes(moduleNode)
I also noticed we use
moduleNodeArray.includes(moduleNode)
, this will cause bugs ifmoduleNode
is not referencing the module node already within the array. So I fixed it by using.some()
Additional context
fixes #12062
What is the purpose of this pull request?
Before submitting the PR, please make sure you do the following
fixes #123
).