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
Fix discovery of components without metadata in repositories #10996
Fix discovery of components without metadata in repositories #10996
Conversation
This is just a shortcut to a final filed in 'dependencyState'. Sometimes it was used and sometimes 'dependencyState.getDepenedency()' was used which is always the same value. This makes this complex code a bit easier to comprehend.
This information needs to be preserved to make sure that an artifact that acts as metadata for itself (metadataSources = artifact()) is found during the early resolution phase where we search for modules in repositories.
Later in the resolution, we already combine all artifacts defined as 'dependency artifacts' on incoming edges. If a component does not have metadata, we need at least information about one artifact early when we look for an artifact (instead of a metadata file).
Such artifacts can actually be addressed by 'dependency artifacts' through the 'artifact { }' notation inside a dependency declaration; or in Gradle Module Metadata.
The order can make a difference in repository selection, when checking if an artifact-only-component containing the artifacts exists in a repository. In absence of a metadata file, we initially test for the existence of one artifact to decide if a repository contains the corresponding artifact-only-component. This is always the first artifact in the set (which is internally converted into a list). This change makes sure that the first artifact is always the same for a build that does not change.
This test reproduces #10948 and other variations of it.
As stated in the here implemented 'ServerWithExpectations' fixture: "handlers, as well as failures, need to be thread-safe" This concrete case was working most of the time since usually there are not more than one expectations for the same request. But if the same request is expected several times, and the requests are received in parallel (as it is the case for metadata download), the handle() method behaved flaky - by not doing reading and updating of the 'run' flag as an atomic operation.
This seems to be dependent on timing. If one request starts after another did already finish, it can take the result from cache.
d9f51e7
to
67b02ff
Compare
public void selectedBy(ResolvableSelectorState selectorState) { | ||
if (firstSelectedBy == null) { | ||
firstSelectedBy = (SelectorState) selectorState; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The behavior of this changed with the selector sorting. So a gaol of this PR is to get rid of this additional (weird) state and instead rely on the order of the selector list.
67b02ff
to
e43500a
Compare
Although these are edge cases, it leads to more consistency and makes the behavior less dependent on order which may change unexpectedly through internal optimisations.
We now rely on the selector sorting when choosing a selector to compute override metadata. The sorting puts the most likely used selector first.
e43500a
to
181205e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good stuff in there! 👍
Two minor remarks
@@ -96,7 +96,7 @@ public LocalComponentDependencyMetadata(ComponentIdentifier componentId, | |||
} | |||
|
|||
private static List<IvyArtifactName> asImmutable(List<IvyArtifactName> artifactNames) { | |||
return artifactNames.isEmpty() ? Collections.emptyList() : ImmutableList.copyOf(artifactNames); | |||
return artifactNames.isEmpty() ? Collections.emptyList() : artifactNames instanceof ImmutableList ? artifactNames : ImmutableList.copyOf(artifactNames); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is already the case inside ImmutableList.copyOf
AFAICT
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I missed something, but it is called copyOf
and I think it always creates a copy.
From the implementation:
ImmutableList<E> list = ((ImmutableCollection<E>) elements).asList();
(which in turn does toArray()
)
And there is no type check for ImmutableList
. Maybe the underlying array is not copied (indicated in the Javadoc, but I can't really find that in the code).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
((ImmutableCollection<E>) elements).asList();
on a ImmutableList
does return this
For components without metadata (metadataSource = artifact), we need to HEAD an artifact to check if a component exists in a repository. This is done in the metadata download phase in resolution (instead of actually downloading a metadata file).
For this to work consistently, information about dependency artifacts needs to be preserved when we construct the so called
ComponentOverrideMetadata
. However, there can be multiple dependencies (or dependency constraints) declarations with different dependency artifact information. We basically always took the first definition we found. However, this does not work, if the first definition does not define an artifact at all (which is always true for dependency constraints). So it never worked for all cases. Recent changes in selector sorting (8eb2d60 of #9307 and probably others) have lead to changes in the ordering and now some cases that worked before do not work anymore (#10948).This PR fixes the issue in general by tracking the first found artifact across all sectors for a module instead of just tracking the first selector.
Note: these changes are only necessary to fix the discovery of components in repositories. The actual retrieving of artifact is done later after resolution. This works (and worked before): All artifacts are retrieved or, if one is missing, an error is thrown.
In addition to the changes required for artifact handling, this PR also improves the handling of 'client modules' and 'changing' status (the other details of ComponentOverrideMetadata).