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

Cargo.lock package order is unstable across different cargo versions #9334

Closed
teor2345 opened this issue Apr 7, 2021 · 5 comments · Fixed by #9384
Closed

Cargo.lock package order is unstable across different cargo versions #9334

teor2345 opened this issue Apr 7, 2021 · 5 comments · Fixed by #9384
Labels
C-bug Category: bug

Comments

@teor2345
Copy link

teor2345 commented Apr 7, 2021

Problem

When a lockfile contains multiple entries with the same package name and version, but different sources, the order of these packages in the lockfile changes between builds. (Even though the dependencies are exactly the same.)

Since our project commits the lockfile to git, these redundant changes produce commit noise, checkout conflicts, and merge conflicts.

Steps

Stable to nightly:

  1. Build ZcashFoundation/zebra@6bb5220 with nightly cargo
  2. Run git diff

Nightly to stable:

  1. Build ZcashFoundation/zebra@64662a7 with stable cargo
  2. Run git diff

Building on a different version changes the lockfile, even though there haven't been any dependency changes.

Note: cargo just needs to do dependency resolution, so a cargo check should work just as well as a build.

Possible Solution(s)

  • Sort the lockfile by name, version, and source
  • Workaround: warn when a build uses packages with the same name and version from different sources

Notes

Output of cargo version:

cargo +nightly --version
cargo 1.52.0-nightly (90691f2bf 2021-03-16)
cargo +stable --version
cargo 1.51.0 (43b129a20 2021-03-16)
@teor2345 teor2345 added the C-bug Category: bug label Apr 7, 2021
@alexcrichton
Copy link
Member

Thanks for the report! Can you clarify a bit, though, to help reproduce this?

I am unable to get Cargo to nondeterministically generate a particular lock file, the ordering of crates in a lock file is always stable given a singular version of Cargo. What I do see, however, is that stable cargo and nightly cargo are producing the different orderings. Is that the behavior you're seeing as well? Or are you seeing the same Cargo produce different versions of a lock file?

@teor2345
Copy link
Author

I am unable to get Cargo to nondeterministically generate a particular lock file, the ordering of crates in a lock file is always stable given a singular version of Cargo. What I do see, however, is that stable cargo and nightly cargo are producing the different orderings. Is that the behavior you're seeing as well? Or are you seeing the same Cargo produce different versions of a lock file?

I think you're right, I was confused because:

  • the commits and cargo both changed the lockfile, and
  • different developers on our team use different cargo versions.

It would be great to have an explicit, deterministic order across cargo versions. But perhaps it's not such a priority if the order only changes between releases, and only for a few crates.

@teor2345 teor2345 changed the title Cargo.lock package order is unstable with different sources Cargo.lock package order is unstable across different cargo versions Apr 11, 2021
@alexcrichton
Copy link
Member

Ok thanks for the info! This is definitely a bug in Cargo and something we strive to not have happen. This is caught up in the larger change for switching the defaults for git dependencies to the HEAD branch instead of a hardcoded master branch.

The PR at fault here is #9133. The change there is that Ord for SourceId changed from being hand-rolled to being derived, and the derived variant is different from the hand-rolled variant. I didn't realize that it would affect situations like this and the lock file.

I'm unfortunately not sure what to do about this change. If we caught this soon after landing I'd say we should revert and fix this before re-landing, but this change has been out for long enough that I don't think it's as feasible to revert any more. At best all I can think of is to hopefully learn from this and ensure that this doesn't happen again in Cargo :(

@teor2345
Copy link
Author

I didn't realize that it would affect situations like this and the lock file.

To be fair, this situation should be rare - it was actually a bug in our dependencies where we changed one crate in our workspace, rather than patching them all.

I don't think it's as feasible to revert any more

I think a revert would just create a bunch of new ordering issues.

And I can understand switching to derived Ord - that way, you never forget a new field. (Forgetting fields would definitely cause you ordering issues.)

Thinking about this a bit more...

How can you make sure the ordering doesn't change in future?

  • new fields need to be added to the end of any types with derived Ord impls
    • add a comment to those types
    • how do you make sure you dependencies don't change their orders?
  • how do you test that encoding/decoding/type changes don't change the order?
    • add some fixed test cases
    • how do you do randomised property testing?

It's really tricky, we had a similar issue recently where we left a new field out of a manual PartialEq impl. proptest didn't pick it up, because the edge case was very low probability. You'd need coverage-guided fuzzing or similar techniques to find it.

It's probably not worth the effort to write a big test harness for these kinds of low-impact, low-probability bugs. But maybe a crater run with a diff would help detect future issues like this? (You might already do that!)

@alexcrichton
Copy link
Member

Discussion at today's Cargo meeting concluded that we should:

This will mean that if projects have switched to using the beta/nightly lock file format they'll need to switch back (sorry about that!) but hopefully the transition period won't be too large. I've tried to add at least a small test to catch changes to this in the future as well.

bors added a commit that referenced this issue Apr 20, 2021
[beta] Revert #9133, moving to git HEAD dependencies by default

This PR reverts #9133 on the beta branch. The reason for this is that I would like to take some more time to investigate fixing #9352 and #9334. I think those should be relatively easy to fix but I would prefer to not be too rushed with the next release happening soon.

We realized today in the cargo meeting that this revert is likely to not have a ton of impact. For any projects using the v3 lock file format they'll continue to use it successfully. For projects on stable still using v2 they remain unaffected. This will ideally simply give some more time to fix these issues on nightly.
bors added a commit that referenced this issue Apr 21, 2021
Fix disagreement about lockfile ordering on stable/nightly

This commit fixes an issue where the order of packages serialized into a
lock file differs on stable vs nightly. This is due to a bug introduced
in #9133 where a manual `Ord` implementation was replaced with a
`#[derive]`'d one. This was an unintended consequence of #9133 and means
that the same lock file produced by two different versions of Cargo only
differs in what order items are serialized.

With #9133 being reverted soon on the current beta channel this is
intended to be the nightly fix for #9334. This will hopefully mean that
those projects which don't build with beta/nightly will remain
unaffected, and those affected on beta/nightly will need to switch to
the new nightly ordering when it's published (which matches the current
stable). The reverted beta will match this ordering as well.

Closes #9334
@bors bors closed this as completed in eb6e1b3 Apr 21, 2021
alexcrichton added a commit to alexcrichton/cargo that referenced this issue Apr 23, 2021
This commit restores the hash value of the crates.io `SourceId` to what
it was before rust-lang#9384. In rust-lang#9384 the enum variants of `SourceKind` were
reordered which accidentally changed the hash value of the `SourceId`
for crates.io. A change here means that users with a new version of
Cargo will have to redownload the index and all crates, which is
something that we strive to avoid forcing.

In changing this, though, it required a manual implementation of `Ord`
to still contain the actual fix from rust-lang#9384 which is to sort `SourceKind`
differently from how it's defined. I was curious as to why this was
necessary since it wasn't ever necessary in the past and this led to an
odd spelunking which turned up some interesting information. Turns out
Rust 1.47 and after had a breaking change where Cargo would sort
dependencies differently. This means that rust-lang#9334 *could* have been opened
up much earlier, but it never was. We ironically only saw an issue when
we fixed this regression (although we didn't realize we were fixing a
regression). This means that we are now permanently codifying the
regression in Cargo.
bors added a commit that referenced this issue Apr 23, 2021
Restore crates.io's `SourceId` hash value to before

This commit restores the hash value of the crates.io `SourceId` to what
it was before #9384. In #9384 the enum variants of `SourceKind` were
reordered which accidentally changed the hash value of the `SourceId`
for crates.io. A change here means that users with a new version of
Cargo will have to redownload the index and all crates, which is
something that we strive to avoid forcing.

In changing this, though, it required a manual implementation of `Ord`
to still contain the actual fix from #9384 which is to sort `SourceKind`
differently from how it's defined. I was curious as to why this was
necessary since it wasn't ever necessary in the past and this led to an
odd spelunking which turned up some interesting information. Turns out
Rust 1.47 and after had a breaking change where Cargo would sort
dependencies differently. This means that #9334 *could* have been opened
up much earlier, but it never was. We ironically only saw an issue when
we fixed this regression (although we didn't realize we were fixing a
regression). This means that we are now permanently codifying the
regression in Cargo.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants