Description
It all started with MENFORCER-426. In short, if we have layout of dependencies like this below (ranges are key here), conflict resolution in verbose mode will misbehave, is sensitive on version ordering in the "dirty tree" (collected graph, output of DependencyCollector).
A -> B -> C[1..2] \--> C[1..2]
(explained: A depends on B and C[1..2] range, and B depends on C[1..2] as well)
As when "dirty tree" is being collected by DependencyCollector (and we have two, old DF and new BF), the ranges are turned into series of artifacts with versions discovered (by VersionRangeResolver), so the input above while collecting A from above would yield some tree like this (in this example some-group:c:jar has versions 1.0 and 2.0 ):
some-group:a:jar:1.0 [compile] +- some-group:b:jar:1.0 [compile] | +- some-group:c:jar:1.0 [compile] | \- some-group:c:jar:2.0 [compile] +- some-group:c:jar:1.0 [compile] \- some-group:c:jar:2.0 [compile]
This "dirty tree" is then transformed using ConflictResolver into final graph. Conflict resolver in "normal" mode works OK and produces stable outputs, that is not sensitive to ordering, this is OK.
But, conflict resolver supports so called "verbose" mode as well, mostly used to perform some "analysis" on graph (like for example "dependency convergence" is). Conflict resolver in "verbose" mode is version ordering sensitive.
Conflict resolver in "verbose" mode would transform that dirty tree above into this:
some-group:a:jar:1.0 [compile] +- some-group:b:jar:1.0 [compile] | \- some-group:c:jar:1.0 [compile] (conflicts 2.0) \- some-group:c:jar:2.0 [compile]
And for analysis like "dependency convergence", this would mean divergency is found (error condition), as there are conflicts. But, if you look more closely on input "dirty tree", you can notice that b depends on c:2.0 as well (as b depends on range [1,2].
Moreover, that dirty tree above is output of resolver standard DF collector. BF on the other hand, produces dirty tree like this:
some-group:a:jar:1.0 [compile] +- some-group:b:jar:1.0 [compile] | +- some-group:c:jar:2.0 [compile] | \- some-group:c:jar:1.0 [compile] +- some-group:c:jar:2.0 [compile] \- some-group:c:jar:1.0 [compile]
(the order of c dependencies are sorted descending).
In other words, DF produces tree with versions in order as discovered (essentially metadata based), the BF explicitly sorts versions in descending order (to maximize skipper effect, but that is BF internal detail).
Same conflict resolver in "verbose" mode transforms BF dirty tree into this:
some-group:a:jar:1.0 [compile] +- some-group:b:jar:1.0 [compile] | \- some-group:c:jar:2.0 [compile] \- some-group:c:jar:2.0 [compile]
And in this case, there is no divergence found. Again, this is same input, but collected with DF and BF collectors, then applied conflict resolution, and once we have divergence, and once we have no divergence. The difference between two inputs is only the order of versions (produced from discovered versions that were resolved from metadata, but new BF explicitly sorts versions in descending order to better utilize it's own internals).
Proper conflict resolver solution should be:
- insensitive on version ordering
- produce "stable" (same) output for same input (version order should not matter, and this is not only about DF vs BF)
- Good to have: the "verbose" mode we currently have is half-assed, in a way, it still removes nodes from tree, and leaves one version (the first in order, this is the problem) that holds conflict information.
- Better to have: We may want a "full" verbose mode, where node selection/elimination and mediation is done, but no node was removed graph still contains all the nodes, as that would give much more precise picture about dependencies, than like today, "randomly" (first) nodes marked for conflict, rest removed.
Attachments
Issue Links
- causes
-
MENFORCER-426 DependencyConvergence in 3.1.0 fails when using version ranges
- Closed
- links to