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

pythonCatchConflictsHook: prevent exponential worst-case #305619

Merged
merged 4 commits into from
May 12, 2024

Conversation

lopsided98
Copy link
Contributor

Description of changes

The hook performs a depth first search on the graph defined by propagatedBuildInputs. This traverses all paths through the graph, except for any cycles. In the worst case with a highly connected graph, this search can take exponential time. In practice, this means that in cases with long dependency chains and multiple packages depending on the same package, the hook can take several hours to run.

Avoid this problem by keeping track of already visited paths and only visiting each path once. This makes the search complete in linear time.

The visible effect of this change is that, if a conflict is found, only one dependency chain that leads to the conflicting package is printed, rather than all the possible dependency chains.

Now that only one dependency chain will be found for each package, a few other simplifications are possible and are included in a separate commit.

Also, add a test to demonstrate the change in output with multiple dependency chains, and fix a minor bug in parsing nix-support/propagated-build-inputs.

The tests pass and I have tested building a few Python packages.

Fixes #305599

cc @DavHau @phaer

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • sandbox = relaxed
    • sandbox = true
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 24.05 Release Notes (or backporting 23.05 and 23.11 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Add a 👍 reaction to pull requests you find important.

Copy link
Member

@phaer phaer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Maybe we should target the python-updates branch here?

@wegank wegank added the 12.approvals: 1 This PR was reviewed and approved by one reputable person label Apr 21, 2024
Add a test where a conflicting package can be found at the end of
multiple dependency chains. This is far too simple an example to
demonstrate the ill effects of exponential time complexity, but does
serve to demonstrate how the error output changes when each path is only
visited once.
The hook performs a depth first search on the graph defined by
propagatedBuildInputs. This traverses all paths through the graph,
except for any cycles. In the worst case with a highly connected graph,
this search can take exponential time. In practice, this means that in
cases with long dependency chains and multiple packages depending on the
same package, the hook can take several hours to run.

Avoid this problem by keeping track of already visited paths and only
visiting each path once. This makes the search complete in linear time.

The visible effect of this change is that, if a conflict is found, only
one dependency chain that leads to the conflicting package is printed,
rather than all the possible dependency chains.
Now that we only visit each path once, a few things can be simplified.
We no longer have to keep a list of different dependency chains leading
to a package, since only one chain will ever be found. Also, the already
visited check also takes care of cycles, so the other cycle check can be
removed.
…itespace

Currently, nix-support/propagated-build-inputs is parsed by splitting on
a single space. This means that if this file contains multiple spaces
separating two paths, the build_inputs list will end up containing an
empty string.

Instead, call split() with no arguments, which splits on runs of
whitespace and also ignores whitespace at the beginning and end of the
string, eliminating the need for strip().
@lopsided98 lopsided98 changed the base branch from staging to python-updates April 27, 2024 01:06
@lopsided98
Copy link
Contributor Author

I updated the target branch

@wegank wegank removed the 12.approvals: 1 This PR was reviewed and approved by one reputable person label Apr 27, 2024
@phaer phaer added the 12.approvals: 1 This PR was reviewed and approved by one reputable person label Apr 27, 2024
@mweinelt mweinelt requested review from Lassulus and DavHau and removed request for jonringer April 27, 2024 21:46
Copy link
Member

@DavHau DavHau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM
Thanks for the fix. It looks good to be merged.

I think it would be better if it would display the first 5 paths leading to a collision instead of just one. I guess this would still be in linear time and it would allow to better grasp the complexity of the underlying dependency conflict and make it easier to debug it.
But I don't want to block this. This could be done in a separate PR

@wegank wegank added 12.approvals: 2 This PR was reviewed and approved by two reputable people and removed 12.approvals: 1 This PR was reviewed and approved by one reputable person labels Apr 29, 2024
@Lassulus Lassulus merged commit 22ced29 into NixOS:python-updates May 12, 2024
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants