Reimplement endorsement of strict version constraints#34893
Conversation
8152fb5 to
e2418c7
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| @@ -207,9 +204,7 @@ private void traverseGraph(final ResolveState resolveState) { | |||
| // Initialize and collect any new outgoing edges of this node | |||
| dependencies.clear(); | |||
| node.visitOutgoingDependencies(dependencies); | |||
There was a problem hiding this comment.
💅 Since we are cleaning the logic here, maybe we could continue cleaning up names: dependencies is really edges - and it becomes that deeper down.
visitOutgoingDependencies could be visitOutgoingDependenciesAndCollectEdges - a mouthful bu that's what it does.
1b963ef to
7d51483
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Adhoc perf test for failing run: https://builds.gradle.org/buildConfiguration/Gradle_Master_Util_Performance_AdHocPerformanceScenarioLinuxAmd64/103025122 |
7d51483 to
7c071f9
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
The following builds have failed: |
|
The following builds have failed: |
|
@bot-gradle test apt prf |
This comment has been minimized.
This comment has been minimized.
|
The following builds have passed: |
|
The following builds have passed: |
|
We still need to add a test demonstrating fixed behavior |
a33f88c to
d54da30
Compare
Prior to this commit, whenever a node was deselected for any reason, if it had an incoming edge that endorsed strict versions, we would "reselect" the source node of the edge. In this case re-selection means removing all outgoing edges and re-queueing the source node. If the source node was the root node, that means we would attempt to de-select the root node and add it back to the queue. This effectively restarts the whole graph traversal from the beginning. If it weren't for some shady logic that short-circuits unstable graphs, this would lead to an infinite loop. However, due the short-circuit we instead produced a mostly correct graph, but sometimes produced incorrect graphs. This occasionally led to critical failures in the resolution engine. In this commit, we re-implement the way strict versions are inherited between nodes in a dependency graph. Strict versions are now handled much more similarly to how exclusions are propagated down a dependency graph. Now, when visiting a node's dependencies, we compute the inherited (ancestor's) strict versions for that node on-demand. We compare the value to the pervious traversal's inherited strict versions, propagating any changes to existing outgoing edges. When adding or removing edges from nodes, we use the endorsing-ness of the edge to determine if additional nodes need to be re-queued to account for changes from endorsed dependencies. This reduces pressure on the unstable-graph-short-circuit logic, avoiding potential for broken graphs when using endorsed strict versions. Additionally, we: - Rework resolveEdges to run in a single pass instead of multiple passes, by instead recomputing selectors in visitDependencies - Improve computeSelector by properly calling use/release on the old/new selectors, ensuring the selector properly tracks the number of edges using it - Update dependency visiting logic to pass-down computed exclusions and ancestors strict versions instead of relying on state, ensuring it is more difficult to accidentally use stale computed state - Rename methods, fields, variables as needed. Add Javadoc and TODOs as needed. Reactor methods as needed for readability.
Calculating these endorsed strict versions for each parent of a node each time a node is processed is expensive. Instead, we cache the result of this computation and ensure we invalidate it each time an input to the calculation is changed.
d54da30 to
e7b27b5
Compare
|
I can confirm that this resolves at least one of the issues in Block's cash-server build. |
This reproducer is mostly minified from the original reproducer, though could likey be reduced down more.
5ba2fba to
506e719
Compare
ljacomet
left a comment
There was a problem hiding this comment.
note that I moved the test according to the TODO it had
Prior to this PR, whenever a node was deselected for any reason, if it had an incoming edge that endorsed strict versions, we would "reselect" the source node of the edge. In this case re-selection means removing all outgoing edges and re-queueing the source node.
See:
gradle/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java
Lines 1092 to 1113 in c055893
If the source node was the root node, that means we would attempt to de-select the root node and add it back to the queue. This effectively restarts the whole graph traversal from the beginning. If it weren't for some shady logic that short-circuits unstable graphs (#29936) this would lead to an infinite loop. However, due the short-circuit we instead produced a mostly correct graph, but sometimes produced incorrect graphs. This occasionally led to critical failures in the resolution engine.
In this PR, we re-implement the way strict versions are inherited between nodes in a dependency graph. Strict versions are now handled much more similarly to how exclusions are propagated down a dependency graph.
Now, when visiting a node's dependencies, we compute the inherited (ancestor's) strict versions for that node on-demand. We compare the value to the pervious traversal's inherited strict versions, propagating any changes to existing outgoing edges.
When adding or removing edges from nodes, we use the endorsing-ness of the edge to determine if additional nodes need to be re-queued to account for changes from endorsed dependencies.
This new approach allows us to avoid the potential of reprocessing the root node, an operation which currently can lead to buggy graphs.
Fixes #33508
Reviewing cheatsheet
Before merging the PR, comments starting with