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)