-
Notifications
You must be signed in to change notification settings - Fork 40.8k
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
Circular reference in JPA, DataSource, initializers #13042
Comments
There is one in I guess we need to figure out what has changed with regards to the scope thing. |
This fails with 1.5 (in a different way): @SpringBootApplication
public class BeanApplication {
@Bean
public static BeanFactoryPostProcessor fooScope() {
return beanFactory -> {
beanFactory.registerScope("foo", new SimpleThreadScope());
};
}
@Bean
@Scope("foo")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(DataSourceProperties properties) {
return DataSourceBuilder.create().url(properties.determineUrl())
.type(org.apache.tomcat.jdbc.pool.DataSource.class).build();
}
public static void main(String[] args) {
SpringApplication.run(BeanApplication.class, args);
}
} It's on a branch 1.5.x in the same repo. |
The 1.5.x version fails because the As far as I can tell, there's no circular reference problem in this case. The problem is that calling public static void main(String[] args) {
String[] dataSources = SpringApplication.run(BeanApplication.class, args).getBeanNamesForType(DataSource.class, false, false);
System.out.println(Arrays.toString(dataSources));
} It will output an empty array. |
The 2.0.x version fails because of a difference in how singleton beans and prototype beans are handled when they are requested while still be created. In the singleton case, there's some fallback logic that allows the singleton that's in creation to be retrieved. In the prototype case, there's no such fallback and a |
In SPR-16783, @jhoeller said that "the recommendation is to never access an object within its own post-processing phase to begin with". This is exactly what |
The "singleton" in question isn't the same as the default scope for beans (I think). It's "raw" singletons (the ones that are registered directly as instances, not bean definitions). The |
I'm pretty sure it's the same. When I remove the |
OK, maybe the raw singletons get cached in the same place as the bean definition ones. I added some non-hibernate samples to my repro project, here: https://github.com/scratches/datasource-cycle/tree/master/src/main/java/org/springframework/boot/autoconfigure/jdbc. They are easier to analyse (fewer beans), but the exception is slightly different. |
Thanks. I'm intrigued by the other two as they look like they should fail in the same way. Time to do some digging. |
|
|
When the 2.0.x version of The problem can be avoided by making |
Thanks for all the research @wilkinsona and @dsyer |
That would fix the issue in Spring Cloud because the other |
I've yet to convince myself that implementing |
Tough decision. I suppose we could have 2 identical invokers, one that is |
Could this be due to a change introduced by the Hikari connection pooler? |
That's very interesting. Thank you, @kentoj. Moving to @SpringBootApplication
public class BeanApplication {
@Bean
public static BeanFactoryPostProcessor fooScope() {
return beanFactory -> {
beanFactory.registerScope("foo", new SimpleThreadScope());
};
}
@Bean
@Scope("foo")
@ConfigurationProperties(prefix = "spring.datasource.dbcp2")
public BasicDataSource dataSource(DataSourceProperties properties) {
BasicDataSource dataSource = properties.initializeDataSourceBuilder()
.type(BasicDataSource.class).build();
return dataSource;
}
public static void main(String[] args) {
SpringApplication.run(BeanApplication.class, args);
}
} But this does not: @SpringBootConfiguration
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourceConfiguration.Dbcp2.class, DataSourceJmxConfiguration.class,
DataSourceInitializationConfiguration.class})
public class BrokenApplication {
@Component
protected static class RefreshScopeConfiguration
implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
registry.registerBeanDefinition("fooScope",
BeanDefinitionBuilder.genericBeanDefinition(FooScope.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.getBeanDefinition());
}
}
@Bean
@Scope("foo")
@ConfigurationProperties(prefix = "spring.datasource.dbcp2")
public BasicDataSource dataSource(DataSourceProperties properties) {
BasicDataSource dataSource = properties.initializeDataSourceBuilder()
.type(BasicDataSource.class).build();
return dataSource;
}
public static void main(String[] args) {
SpringApplication.run(BrokenApplication.class, args);
}
}
class FooScope implements org.springframework.beans.factory.config.Scope, BeanFactoryPostProcessor {
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
return objectFactory.getObject();
}
@Override
public Object remove(String name) {
return null;
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return null;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
beanFactory.registerScope("foo", this);
}
} |
|
For the record, a workaround has been implemented in Spring Cloud for this, see spring-cloud/spring-cloud-commons@0e259ea |
I've opened a Framework issue so that we can address this without resorting to the |
@dsyer @wilkinsona Have you solved this problem? if not,i can only user spring cloud 1.5,is that it? |
@xcbeyond As noted above, a workaround has been implemented in Spring Cloud. |
We'd like to fix this by introducing an
This occurs when a specific username and/or password is provided for the initialisation of the DataSource. When it occurs, a new DataSource is created with the default settings. This can lead to a pool with many connections being created purely for initialisation. We'd like to address this by tuning any pool that's created specifically for initialisation to have a more sensible number of connections (probably 1).
This approach would, even when replacing a bean, would allow us to know if the user wanted the datasource to be initialised. The annotation could have an attribute that specifics the prefix for the properties that configure datasource initialisation. These same properties could be used to apply the initialisation to an auto-configured test database. |
I'm able to work around this issue by specifying management.server.port different than the server.port |
Hey all, we are also facing this issue and found out that beside the already mentioned information spring-boot 2.0 is using management.health.db.enabled=false Afterwards you can define your own HealthChecks with the Edit: The HealthIndicator is also not working because of injection of the datasources. Hope to see the issue fixed soon. |
@klopfdreh Thanks for the information. This is the first time that Actuator has apparently been involved in this issue. Could you share a sample that reproduces the problem you are describing? I'd like to see if it's a variant of one of the problems described above, or another case that we haven't seen before. |
Hi @wilkinsona - As mentioned in the original issue description and the mentioned link: spring-cloud/spring-cloud-commons#355 org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthIndicatorAutoConfiguration.getValidationQuery(DataSourceHealthIndicatorAutoConfiguration.java:115) and the following lines. Sadly I can't provide the source code but I just wanted to give the hint about the actuator. |
@klopfdreh What version of Spring Cloud are you using? That part of the problem should have been fixed already by spring-cloud/spring-cloud-commons@0e259ea. |
@wilkinsona - no spring cloud, but spring-boot 2.1.x, but there the actuator endpoint is also affected. Could be that this is a project related issue. If I have more information I can share I am going to do so. |
Thanks. Are you doing anything with your own custom scopes or prototype beans? IIRC, the various examples listed above all require one or the other for the problem to occur. You may be facing the same problem as described in this issue, or it may be something else. We've yet to see Actuator be the cause of the problem and I suspect it's not in your case and that what you're seeing is just a symptom. Unfortunately, we won't be able to do any more than speculate without having some code to look at so fingers-crossed that you're able to share something. It doesn't have to be your exact code, just enough to reproduce the problem that you're seeing. |
I just tried to update to spring-boot 2.1.5 but facing the same error. I just had a look in the initialization of the custom DataSource bean. The bean itself is created within a class using In this case I guess the default scope is used (singleton) and it is not a prototype bean. All in all there is no specific scope handling introduced. I know that you can't do anything more without stacktrace, but I just wanted to provide those information about the actuator endpoint and that this is not only occuring in spring-cloud. This is only an assumption, but is there any specific handling for datasources in kind of bean creation? This error seems to occure only with an additional DataSource provided as bean and in this particular scenario with the actuator endpoint, because @samo-esure also mentioned to change the managament port works (this also worked for me) - also the spring-cloud issue listed this in the stack. Also to disable the health endpoint or the db indicator worked. So I assume that this is an issue around the DataSource injection within the actuator implementation or an issue related to the startup of this spring module. |
@wilkinsona - We found the issue and it is really hard to explain in detail. Basically it was an issue that the health endpoint was causing the DataSource bean to be created way earlier than without. Because of that shift in the order of the bean creation the configuration class in which the datasource was created was invoked a bit earlier and a setter (also autowired with required false) was resolved. Finally this setter was causing the circular dependency. So the error was missleading and might also misslead in other scenarios to the assumption that the actuator health endpoint is the issue. So my advice would be to ask devs facing this issue - for those who not defined custom scopes - to have a look in the context at which a custom datasource is created and search for circular dependencies in that area (especially with optional beans). |
We just ran into this while trying to use There are 2 configurations classes, The key here I believe is the Our fix for now is to disable the |
This is noteworthy as the release notes should mention the new spring.datasource.initialization-order that controls whether initialization is performed before or after JPA. |
This was originally reported by a Spring Cloud user (someone trying to upgrade the Petclinic microservice edition to Spring Boot 2.0 - so it works with 1.5): spring-cloud/spring-cloud-commons#355.
I'm still a bit mystified as to why it happens, but the evidence is pointing to something in the
DataSourceInitializer
and its configuration (there's aBeanPostProcessor
that forcibly instantiates theDataSourceInitializerInvoker
when it sees aDataSource
).Here's a really small app that blows up in the same way (with no Spring Cloud dependencies). You need a
schema.sql
and adata.sql
to make it fail, and you need to setspring.datasource.initialization-mode=embedded
(oralways
) to trigger the initialization. Dependencies are just h2 and the JPA starter.If you
(exclude= {HibernateJpaAutoConfiguration.class, JpaRepositoriesAutoConfiguration.class})
the problem goes away. Here's some code in github: https://github.com/scratches/datasource-cycle.While I was investigating the Cloud issue I also managed to find some combinations of auto configurations that led to other cycles (not involving JPA, but still with a scoped data source), so there is more than one way to tickle this.
The text was updated successfully, but these errors were encountered: