Hi team!
I am developing a simple Spring Boot component that adds some DataSource
beans to application context. Currently I am facing a problem with the way Spring Boot tries to implement the initialization of databases, i.e. the DataSourceInitializerInvoker
bean that tries to retrieve primary bean of DataSource
type during very first DataSource
bean initialization.
That leads to a BeanCurrentlyInCreationException
in my case, when @Primary
DataSource
bean is dependent on some other DataSource
bean. The dependency cycle is as follows: "primary DataSource" -> "ordinary DataSource" -> "DataSourceInitializerInvoker" -> "primary DataSource".
A simple project that reproduces the problem can be found here - https://github.com/galingerv/datasource-issue-reproducer
I wonder if DataSourceInitializerInvoker
can be turned off to avoid this problem, that would be acceptable in my case.
Comment From: philwebb
Fixing the cycle issue might be hard to do in 2.2.x, but it would be nice to offer an easy way to switch off initialization.
Comment From: philwebb
Actually @snicoll just reminded me that we already have a property. @galingerv have you tried adding spring.datasource.initialization-mode=never
to your application.properties
?
Comment From: galingerv
@philwebb thanks for a quick response! Yes, I've tried that option with no success, and quick code analysis of version 2.2.6 shows me that that property is taking into consideration in the instance of internal class DataSourceInitializer
that is created right after DataSourceinitializerInvoker
bean retrieves the primary DataSource
. So the cyclic dependency still exists. Even if we could analyse that property directly in DataSourceInitializerInvoker
, I will have to override the default value of a property org.springframework.boot.autoconfigure.jdbc.DataSourceProperties#initializationMode
to NEVER
in my autoconfiguration which also seems to be tricky.
Comment From: snicoll
@galingerv thanks for the feedback and sorry we sent you in the wrong direction. We should certainly improve that to make sure the bean is not registered at all if the property is set to never
. I've created #21363 for that.
The current arrangement is that datasource initialization occurs regardless of if Spring Boot has configured the datasource, I think the most reasonable action at this point is to exclude DataSourceAutoConfiguration
and eventually apply some of the things you may want (typically an import on DataSourcePoolMetadataProvidersConfiguration
).
I will have to override the default value of a property org.springframework.boot.autoconfigure.jdbc.DataSourceProperties#initializationMode to NEVER in my autoconfiguration which also seems to be tricky.
Having an auto-configuration that provides multiple datasources is tricky I am afraid. This is a topic where an improvement in Spring Boot is required. Can you please give the exclude a try and report back?
Comment From: galingerv
@snicoll excluding the DataSourceAutoConfiguration
within user application will definitely help my autoconfiguration to successfully start. But I hope to develop a starter that brings no need to exclude certain autoconfigurations in user SpringBoot application.
Comment From: snicoll
I understand that and that's what I meant by "Having an auto-configuration that provides multiple datasources is tricky I am afraid. "
The problem is that you're trying to do in an auto-configuration something a bit more "core" that we don't support. I am afraid I don't have another suggestion for you at this time.
Even if we offered a way to easily disable it, you'd still have to override a default behaviour of Spring Boot and that's unusual in a starter indeed so I am not sure what you're asking us really.
Comment From: galingerv
Hi!
If I understand the concept of Spring Boot correctly, almost everything that comes "out-of-the-box" should be overridable or customizable. Out of the box, Spring Boot lets define a single DataSource
bean so I really don't understand what is the problem with defining some additional DataSource
beans in an autoconfiguration. Defining additional beans, I believe, is what most autoconfigurations actually do. Can you please clarify my understanding of concept?
Comment From: snicoll
You are correct. However what you've just described is not the problem you are facing. I am arguing that you're doing something in an auto-configuration (multiple datasources) that is not supported in Spring Boot at the moment. My recommendation is to disable the auto-configuration but you're not keen to do that.
If you'd like datasource init to back-off without disabling the initialization mode or without excluding the auto-configuration, there's no plan in offering that at the moment but I am happy to revisit if you have ideas.
Comment From: galingerv
By the way, I found a way to disable the DataSourceInitializerPostProcessor
, quite hackish and unsafe though. As of 2.2.x, this class prevents the DataSourceInitializerPostProcessor
from being registered as a BeanPostProcessor
:
static class Registrar implements ImportBeanDefinitionRegistrar {
private static final String BEAN_NAME = "dataSourceInitializerPostProcessor";
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(BEAN_NAME)) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(Object.class);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
} else {
registry.removeBeanDefinition(BEAN_NAME);
}
}
}
With this ImportBeanDefinitionRegistrar
registered in my autoconfiguration, the dependency cycle doesn't arise.
Comment From: galingerv
@snicoll could you please explain why do you believe multiple datasources are not supported in SpringBoot autoconfiguration? I can imagine an application where multiple DataSource
beans autoconfigured by the starter are successfully started and can be used in the app and by another autoconfigurations, so I can't understand what are the reasons for that not supported status.
Comment From: snicoll
That’s not what I wrote is it? I mentioned that spring boot itself does not auto configure multiple data sources, we just don’t have support for that. Doing it in an app and an auto-configuration is a very different thing.
Back to my question, if you don’t want to exclude the auto config or set a property for the time being, we’ll have to investigate based on a sample since we’re not making great progress here.
Please share a sample app we can run ourselves that reproduces the problem.
Comment From: galingerv
Here is a sample that demonstrates how autoconfiguration looks like:
https://github.com/galingerv/datasource-issue-reproducer/tree/using-autoconfiguration
Autoconfiguration: com.example.datasources.autoconfiguration.MultipleDatasourcesAutoConfiguration
Application that uses it: com.example.demo.DemoApplication
Comment From: wilkinsona
@snicoll Did you get a chance to look at the sample? I'm wondering if there's something here that we need to consider as part of our 2.5.x data source theme.
Comment From: snicoll
@wilkinsona I might but I don't remember. I agree that it should be considered as part of our data source theme.
Comment From: wilkinsona
Thanks. I've assigned it to 2.5.x as an enhancement for now. We can adjust as needed as the theme progresses.
Comment From: wilkinsona
The changes to DataSource initialization in 2.5.0-M2 have fixed this. The sample fails with 2.5.0-M1:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.0-M1)
2021-03-09 15:00:37.772 INFO 84880 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 1.8.0_252 on wilkinsona-a01.vmware.com with PID 84880 (/Users/awilkinson/dev/temp/datasource-issue-reproducer/target/classes started by awilkinson in /Users/awilkinson/dev/temp/datasource-issue-reproducer)
2021-03-09 15:00:37.773 INFO 84880 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2021-03-09 15:00:38.138 WARN 84880 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataSource' defined in com.example.demo.DemoApplication: Unsatisfied dependency expressed through method 'dataSource' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'firstDataSource' defined in com.example.demo.DemoApplication: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker': Invocation of init method failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'dataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?
2021-03-09 15:00:38.144 INFO 84880 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-03-09 15:00:38.150 ERROR 84880 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| dataSource defined in com.example.demo.DemoApplication
↑ ↓
| firstDataSource defined in com.example.demo.DemoApplication
↑ ↓
| org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker
└─────┘
It starts up successfully with 2.5.0-M2 due to these changes:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.0-M2)
2021-03-09 14:59:21.583 INFO 84719 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 1.8.0_252 on wilkinsona-a01.vmware.com with PID 84719 (/Users/awilkinson/dev/temp/datasource-issue-reproducer/target/classes started by awilkinson in /Users/awilkinson/dev/temp/datasource-issue-reproducer)
2021-03-09 14:59:21.584 INFO 84719 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2021-03-09 14:59:21.986 INFO 84719 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.587 seconds (JVM running for 0.824)