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

Delay resolving patterns to existing versions until all final version… #3477

Merged
merged 1 commit into from
May 23, 2017

Conversation

blexrob
Copy link
Contributor

@blexrob blexrob commented May 22, 2017

…s are known. Fixes #3466

Summary

When PackageRequester has retrieved the version information for a pattern, it queries the package resolver for the current best match for its pattern. If it finds one, it immediately resolves to that version.

This can cause indeterminism. If we have requests for graceful-fs@4.1.10, graceful-fs@^4.1.2 and graceful-fs@^4.1.11, the following can happen.

  1. graceful-fs@4.1.10 resolves first, to 4.1.10
  2. graceful-fs@^4.1.2 resolves next, finds 4.1.10, resolves to that.
  3. graceful-fs@^4.1.11 resolves last to 4.1.11.

In a different order, the following can happen:

  1. graceful-fs@4.1.10 resolves first, to 4.1.10
  2. graceful-fs@^4.1.11 resolves next to 4.1.11.
  3. graceful-fs@^4.1.2 resolves last, finds 4.1.11, resolves to that.

This PR reworks the package-requester to store requests that match an existing version with the resolver. When the resolver has processed all other patterns, it then resolves those stored requests. These requests will then always find the best version.

Test plan
Added a test that installs and checks the package.json from #3466. This test isn't deterministic, but if the error is present it should find it now and then. I have run the test a few time without the fix and seen it fail. The whole testsuite runs without error.

Risks
I have adjusted the default package resolver and the resolver from import.js, a risk is that I have overlooked other (less well-tested) paths.

@voxsim
Copy link
Contributor

voxsim commented May 23, 2017

Mmh for what I understand from your pr we ordered patterns in a different way, am I correct?

Do you think we can code something that is deterministic and indipendent from the order? (maybe we need to add a phase after, I am just guessing).

Btw thanks for your work, you are awesome!!!

@blexrob
Copy link
Contributor Author

blexrob commented May 23, 2017

As far as I can tell from the code, the intention is that the intent is to resolve patterns to already found versions, and only introduce new versions if none of the current versions are a match. So, the logic is:

  • look up existing versions. A match? use that one
  • go to the registry to get the best version match, add that to the set of current versions

(the current code always does the registry lookup part, but that is only needed for yarn import. Could be a perf improvement to skip it for yarn add/install, but that's something for another issue).

The indeterminism comes from immediately using matching versions - because resolutions without matches can introduce extra versions, which could be better matches.

This PR makes the packagerequest report the packagerequest that it found a match to the resolver (with the function reportPackageWithExistingVersion). The resolver then records that package request in the delayedResolveQueue.

When the resolver has run all packagerequests, it then runs resolvePackagesWithExistingVersions (this is the phase after you mentioned). This will call resolveToExistingVersion in the package requests that found an existing version. This function will re-run the search for existing versions. A match was found previously, so it will be found again. At this time, all packagerequests have found their version info, so all versions that are needed to satisfy all requests are know to the resolver. So, the packagerequest will find the best matching version every time.

As far as I understand the code, the final set of resolved versions should be the same every run (when the input and the registry responses are the same, of course). And therefore, the best version match for a pattern will also be the same, and therefore deterministic.

@bestander
Copy link
Member

Huge thanks, that looks great and should remove randomness from the package resolution.

@bestander bestander merged commit a080a83 into yarnpkg:master May 23, 2017
@raido
Copy link
Contributor

raido commented May 23, 2017

When will this ship? It seems this might the issue for me, because yarn.lock seems to "fluctuate" if new packages are added from different branches, even if taking the master branch yarn.lock as base file just before a merge of another branch.

@bestander
Copy link
Member

In 0.26, I think we can release RC on Friday

@blexrob
Copy link
Contributor Author

blexrob commented May 23, 2017

@bestander @voxsim I just found an oversight in my analysis: with 2 patterns, a@1.0.1 (A) and a@^1.0.0 (B), current version 1.0.2, the following can happen:
Order 1

  1. A resolves, adds version 1.0.1
  2. B resolves, finds matching version 1.0.1, delays
  3. final resolve for B, to version 1.0.1
    Outcome: a@1.0.1 -> 1.0.1, a@^1.0.0 -> 1.0.1

Order 2

  1. B resolves, adds version 1.0.2
  2. A resolves, adds version 1.0.1
    Outcome: a@1.0.1 -> 1.0.1, a@^1.0.0 -> 1.0.2

The committed code will still mitigate problems, but it won't completely solve them...

I'm afraid a better algorithm is needed to get fully deterministic resolves.

@voxsim
Copy link
Contributor

voxsim commented May 23, 2017

yep :) I think to be deterministic we need to put in place some rules that are invariant respect the order otherwise it will be always not deterministic. For example from your last message we should pick always version 1.0.2 for B. I don't know if it is possible, i will dig further in the topic in the next weeks.

@voxsim
Copy link
Contributor

voxsim commented May 23, 2017

Btw i want to thank you so much, one of our task is to make package-* files stateless and this analysis helps a lot :D

@bestander
Copy link
Member

Ok, I'll file a separate issue

@bestander
Copy link
Member

Done #3489

bestander added a commit to bestander/yarn that referenced this pull request Jun 2, 2017
… versions are known. Fixes yarnpkg#3466 (yarnpkg#3477)"

This reverts commit a080a83.

The solution was still not deterministic enough (see discussion) and test was not failing before the change reliably.
Next commit comes up with a solution.
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

Successfully merging this pull request may close these issues.

Package version resolve from registry is not deterministic
4 participants