See https://github.com/spring-projects/spring-boot/issues/38880#issuecomment-1865887963
If we fix this we may want to revert #38880 given this comment which I missed when apply the fix.
Comment From: philwebb
Repeating the comment from @alexandreBaronZnk here:
Hi, I've the same issue described here #38861.
The solution to explicitly add a dependency between HibernateJpaConfiguration and DataSourceTransactionManagerAutoConfiguration is enough for this particular case.
But I think there is a more global problem in the AutoConfigurationSorter.
There is a comment in an another issue for the same bug here : https://github.com/spring-projects/spring-boot/issues/33291#issuecomment-1323435208.
It indicates that the @AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) should be enough to indicate that HibernateJpaConfiguration will be execute before DataSourceTransactionManagerAutoConfiguration.
The @AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) works like a charm when there is no constraint to TransactionAutoConfiguration in the current case.
Because in the AutoConfigurationSorter the following method sort by order
List<String> getInPriorityOrder(Collection<String> classNames) {
AutoConfigurationClasses classes = new AutoConfigurationClasses(this.metadataReaderFactory,
this.autoConfigurationMetadata, classNames);
List<String> orderedClassNames = new ArrayList<>(classNames);
// Initially sort alphabetically
Collections.sort(orderedClassNames);
// Then sort by order
orderedClassNames.sort((o1, o2) -> {
int i1 = classes.get(o1).getOrder();
int i2 = classes.get(o2).getOrder();
return Integer.compare(i1, i2);
});
// Then respect @AutoConfigureBefore @AutoConfigureAfter
orderedClassNames = sortByAnnotation(classes, orderedClassNames);
return orderedClassNames;
}
But when there is a constraint, the recursive flow doesn't respect ordering.
private void doSortByAfterAnnotation(AutoConfigurationClasses classes, List<String> toSort, Set<String> sorted,
Set<String> processing, String current) {
if (current == null) {
current = toSort.remove(0);
}
processing.add(current);
for (String after : classes.getClassesRequestedAfter(current)) {
checkForCycles(processing, current, after);
if (!sorted.contains(after) && toSort.contains(after)) {
doSortByAfterAnnotation(classes, toSort, sorted, processing, after);
}
}
processing.remove(current);
sorted.add(current);
}
The method getClassesRequestAfter use a HashMap to iterate over autoconfiguration, that make the result not previsible.
private final Map<String, AutoConfigurationClass> classes = new HashMap<>();
Set<String> getClassesRequestedAfter(String className) {
Set<String> classesRequestedAfter = new LinkedHashSet<>(get(className).getAfter());
this.classes.forEach((name, autoConfigurationClass) -> {
if (autoConfigurationClass.getBefore().contains(className)) {
classesRequestedAfter.add(name);
}
});
return classesRequestedAfter;
}
And it is strange that the ordering in the after annotation impacts the final autoconfiguration ordering too.
A debugging in my application for the following instruction classes.getClassesRequestedAfter("org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration") produces this result
org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration,
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,
com.google.cloud.spring.autoconfigure.firestore.FirestoreTransactionManagerAutoConfiguration,
com.google.cloud.spring.autoconfigure.datastore.DatastoreTransactionManagerAutoConfiguration,
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,
com.google.cloud.spring.autoconfigure.spanner.SpannerTransactionManagerAutoConfiguration
The DatastoreTransactionManagerAutoConfiguration is before HibernateJpaAutoConfiguration and this order is important in the final autoconfiguration ordering.
IHMO, the current fix should be enough to this particular case, but a more global fix should be done in the AutoConfigurationSorter class.
Comment From: philwebb
I've got a change lined up in https://github.com/philwebb/spring-boot/tree/gh-38904 but it's been quite hard to replicate the problem in a test.
@alexandreBaronZnk Do you have a project that replicates the ordering issues you were debugging that you could share?
Comment From: alexandreBaronZnk
@philwebb Reproducing this behavior is what make me crazy since a couple of days.
Just for an exemple, on my business application, a version doesn't have the issue, and just adding a prometheus endpoint to override the one provided by actuator (to remove exemplars that are not supported by my prometheus version) produce the issue.
And with this buggy version, just remove the dependency spring-cloud-gcp-starter-trace fix it.
But I think that this unit test can reproduce it : https://github.com/spring-projects/spring-boot/compare/main...alexandreBaronZnk:spring-boot-issue-38904:test-38904
@philwebb I hope it could be useful.
Comment From: philwebb
I hope it could be useful.
@alexandreBaronZnk You bet it's useful!! Thanks so much for your efforts with this one.