> From: lkrat...@blueorigin.com
> To: dev@ant.apache.org
> Subject: RE: Possible Ivy bug (and suggested fix) in ChainResolver
> Date: Wed, 25 Mar 2015 18:16:08 +0000
> 
> I found the root cause of the fishiness. It was not a bug (and was not 
> actually in ChainResolver) but is in my opinion a rather unfortunate and 
> poorly chosen default setting that is to blame (the latest-strategy).
> 
> During resolution in the chain resolver, the current sub-resolver tries to 
> determine whether it should be skipped and the previous artifact be selected 
> over the current artifact. Assuming that the "force" and "dynamic" tests do 
> not result in an early rejection of the current artifact, the "latest" of the 
> two artifacts is selected by sorting the artifacts in a List using a 
> Comparator suited to the current latest-strategy (latest-revision, 
> latest-time, etc).
> 
> The problem here is quite simple. The default latest-strategy is 
> "latest-revision". So when a second artifact has been resolved, and it is of 
> the same revision as the first, nothing gets sorted. The result is that you 
> will get the first artifact found based upon the ordering of your 
> repositories in the chain instead of the newer of the two artifacts.
> 
> I think that either latest-time needs to be the default strategy or the 
> latest revision comparator needs to do a secondary sort to sort by 
> lastModified time.
> 
> Possibly allow configuration of this behavior (should there be a secondary 
> sort by time to avoid stale artifacts, or not so that repository order breaks 
> the tie). This is important (critical) behavior and should be configurable.
> 
> Defaulting to latest-revision will not only deliver undesired stale 
> artifacts, but it is unclear to the user why they are getting stale artifacts 
> or how to make it stop happening. The latest-time strategy will give you the 
> latest revision 99% of the time and the latest artifact 100% of the time. But 
> the latest-revision strategy will give you the latest artifact 100%, 50%, 
> 33%, or 25% of the time when the revision numbers are the same, depending 
> upon how many resolvers you have (1/n), and assuming that any repository may 
> contain the latest artifact.
> 
> Furthermore, the docs do not give a great description of "force". I learned 
> much about the actual behavior of this attribute while debugging. First thing 
> I learned was that it is not a good name.
> 
> What force actually does is it allows a resolver to be considered when a 
> previous resolver has found an artifact. After the first artifact has been 
> found, only repositories with force=true will have a chance of competing (for 
> instance, they might have a newer version of 1.0.0-SNAPSHOT). Otherwise, they 
> are discarded immediately and no date comparison is attempted.
> 
> Force should actually be named "considerAlways" or "considerAnyway". That 
> seems to be a more suitable name. No action requested here, but pointing out 
> that this attribute has a misleading name.
> 
> Summary:
> 1 - Please reconsider changing the default latest-strategy to be latest-time.
> 
> 2 - Please consider adding a secondary "lastModified" sort to 
> LatestRevisionStrategy.ArtifactInfoComparator whether or not you change the 
> default latest-strategy to latest-time or not.
> 
> 3 - Please document, illustrate, and demonstrate in one place the behaviors 
> of chain resolver in combination with force, returnFirst, 
> defaultLatestStrategy, ivy.resolver.default.check.modified, useOrigin, and 
> other settings and attributes that affect resolution behavior. (I am working 
> on this document now.)
> 
> 4 - Please consider creating independent caches by default for each 
> repository. I have not drilled down on this issue yet, but I suspect that it 
> fixes serious cache collision issues that I think I saw while debugging 
> (found and selected local repo artifact, checked cache before delivery, ended 
> up delivering cached stale artifact that came from a totally different repo 
> :( ).
> 
> Thanks,
> 
> L.K.

MG>i think we can take hints from maven brothers on a tested strategy
MG>to referencing dev jars during development..their solution is to employ 
SNAPSHOT version during CI cycles
MG>SNAPSHOTs are available until the jar is promoted to RELEASE at which point 
a tag is assigned to version
MG>SNAPSHOT delivers ${project.id}-YYYYMMDD.hhmmss.jar so unless you have 
multiple machines able to gen
MG>deployables within a second the last second is the arbiter which clearly 
identifies the latest jar
MG>Snapshot versions are ephemeral until the next snapshot build so remote 
lookup would not be implemented
http://books.sonatype.com/mvnref-book/reference/pom-relationships-sect-pom-syntax.html

MG>repository caches when stored within a regular Nexus Repository are typed as 
Proxy/Hosted/Virtual
MG>ProxyApache,ProxyCentral or ProxyCodehaus
MG>Hosted3rdParty,HostedRelease,HostedSnapshot
MG>VirtualRepo (Virtual repos are generally for OSGI bundles)
MG>Once you know the general type Proxy or Hosted or Virtual you can then 
select sub-type (such as Hosted3rdParty,HostedRelease,HostedSnapshot)
https://books.sonatype.com/nexus-book/reference/confignx-sect-manage-repo.html
MG>thank you for taking the necessary time to think this through

> 
> From: Loren Kratzke
> Sent: Tuesday, March 24, 2015 1:09 PM
> To: 'dev@ant.apache.org'
> Subject: Possible Ivy bug (and suggested fix) in ChainResolver
> 
> I have a some observations about how the chain resolver selects a dependency. 
> I think this may be a bug but I am not sure because the intent of the source 
> code is not entirely clear. It reads one way, but behaves in a different way. 
> I have pinpointed the exact spots in code where this happens.
> 
> Here is my simple test setup used to debug this issue. I have two resolvers 
> (Filesystem and URL) configured in a ChainResolver in that order. I publish 
> to one resolver and then the other repeatedly and consume the result in 
> another project. I use checkModified=true and changingPattern=".*" on both 
> resolvers.
> 
> My artifact is simply a text file with the current date and time so it is 
> easy to see whether you get fresh or stale artifacts from the repos.
> 
> When I consume the published artifact from the other project, I will get the 
> artifact from the first configured resolver in the chain (Filesystem in this 
> case). But I know from debugging that the second resolver is also evaluated. 
> So as an experiment, I added force="true" on the second resolver to see if I 
> could force Ivy to ignore the first result and favor an artifact returned by 
> the second resolver. Instead, Ivy returned the artifact from the first 
> resolver even though the second artifact was newer AND the second resolver 
> had force="true".
> 
> When I debugged this to see why the first artifact was chosen over the second 
> artifact, I found something very fishy.
> 
> ChainResolver.getDependency() iterates over each resolver in the chain. First 
> it found the Filesystem resolver and the artifact and next it found the URL 
> resolver and artifact. Next it calls BasicResolver.getDependency() which will 
> compare the previously resolved artifact with the current artifact.
> 
> This is where it gets very fishy. At the end of the getDependency() method it 
> calls AbstractResolver.checkLatest() which I assume is intended to return the 
> latest of the two artifacts. But that comparison never happens. 
> AbstractResolver.isAfter is invoked with two artifacts to be compared and a 
> null Date. Since the date is null, the two artifacts are never compared and 
> no matter what, the first artifact will be returned and the second one 
> discarded and a verbose message will be emitted stating that the second 
> artifact is older than the first artifact, every time. The message is on line 
> 533 of AbstractResolver. (I am looking at Ivy-2.3.0 so if that line does not 
> make sense on trunk then let me know.)
> 
>     Message.debug("\tmodule revision kept as younger: " + newModuleDesc);
>     saveModuleRevisionIfNeeded(dd, newModuleFound);
>     return newModuleFound;
> 
> The message is not true. The artifact that was kept was the older of the two 
> and a comparison of lastModified never happened (and never can happen in the 
> current code as far as I can tell).
> 
> So the actual logic in AbstractResolver.checkLatest() simply returns the 
> first artifact found. While this is not a bad behavior, it does not seem like 
> it is the intended behavior. I mean, why go through all the trouble of 
> pretending to compare two artifacts using date methods when the logic never 
> executes because the passed in Date object is null. And why emit a message 
> stating that one was determined to be older than the other. That is super 
> fishy.
> 
> Furthermore, the next line in ChainResolver.getDependency() after 
> resolver.getDependency() is called (ChainResolver line105) references 
> isReturnFirst(). That is fishy because none of that matters any more. The 
> current artifact was rejected on the previous line of code and the previous 
> (aka first) artifact is now the current artifact and is the one that will be 
> returned (without a date comparison, and for the arbitrary reason that is was 
> found before the other one).
> 
> I think that the intent of the null Date object is to compare each artifact 
> to a static Date configured elsewhere (I have no idea where), but if the code 
> were to actually compare the lastModified dates of the two artifacts, a 
> useful result would happen - Ivy would return the latest artifact from across 
> multiple repositories.
> 
> That is huge because I have never been able to get Ivy to do this. I have 
> never seen anybody get Ivy to search multiple repositories and return the 
> latest artifact. This is useful for local development when you publish 
> locally to consume locally modified artifacts. It would be nice to have the 
> option of picking up newer artifacts from a central repo when those occur 
> without having to blow away a local repository and its cache.
> 
> (By the way, giving my local repo its own cache seems to have solved some 
> other strange issues I was having. I recommend this to everybody and I think 
> it should be a default in Ivy, but that is debatable and would need some more 
> research and concensus.)
> 
> I think that this is a good feature and should be configurable. I think 
> possibly it was intended to be configured via 
> ChainResolver.returnFirst="true|false" but that code executed when it was too 
> late and the decision had already been made. If I were to make this a 
> feature, and make it configurable, I would configure this using an attribute 
> named returnFirst because that is the exact facet of functionality that we 
> are talking about here.
> 
> Thanks for your attention. Hope I am helping here. I am considering coding 
> this to see if it works as expected. I would be happy to report my results 
> and provide a patch if anybody is interested in evaluating this.
> 
> L.K.
                                          

Reply via email to