Skip to content

DataSourceInitializerInvoker fires too early and cannot be avoided #12862

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

Closed
davidkarlsen opened this issue Apr 14, 2018 · 3 comments
Closed
Labels
status: duplicate A duplicate of another issue

Comments

@davidkarlsen
Copy link
Contributor

davidkarlsen commented Apr 14, 2018

spring-boot: 2.0.1

The cause of the problem is that I have two datasources, one is @Primary, and it is a wrapper for the secondary one (to collect metrics for oracle):

@Configuration
@ConfigurationProperties( "data-source" )
public class DataSourceConfig {

    //Do *NOT* use SQLForValidateConnection - bad for performance, Oracle has build in mech instead: https://docs.oracle.com/database/121/JJUCP/connect.htm#JJUCP8136
    private int minPoolSize;
    private int maxPoolSize;
    private int initialPoolSize;
    private boolean validateConnectionOnBorrow = true;
    private int maxConnectionReuseTimeSec;
    private int inactiveConnectionTimeoutSec;
    private int abandonedConnectionTimeout;
    private int timeToLiveConnectionTimeout;
    private int connectionWaitTimeout;
    private String ONSConfiguration;
    private boolean fastConnectionFailoverEnabled = false;
    private int maxStatements;

    private String user;
    private String password;
    private String URL;
    private int loginTimeout;
    private Long readTimeoutMillis;

    public void setMinPoolSize(int minPoolSize) {
        this.minPoolSize = minPoolSize;
    }

    public void setMaxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
    }

    public void setInitialPoolSize(int initialPoolSize) {
        this.initialPoolSize = initialPoolSize;
    }

    public void setValidateConnectionOnBorrow(boolean validateConnectionOnBorrow) {
        this.validateConnectionOnBorrow = validateConnectionOnBorrow;
    }

    public void setMaxConnectionReuseTimeSec(int maxConnectionReuseTimeSec) {
        this.maxConnectionReuseTimeSec = maxConnectionReuseTimeSec;
    }

    public void setInactiveConnectionTimeoutSec(int inactiveConnectionTimeoutSec) {
        this.inactiveConnectionTimeoutSec = inactiveConnectionTimeoutSec;
    }

    public void setAbandonedConnectionTimeout(int abandonedConnectionTimeout) {
        this.abandonedConnectionTimeout = abandonedConnectionTimeout;
    }

    public void setTimeToLiveConnectionTimeout(int timeToLiveConnectionTimeout) {
        this.timeToLiveConnectionTimeout = timeToLiveConnectionTimeout;
    }

    public void setConnectionWaitTimeout(int connectionWaitTimeout) {
        this.connectionWaitTimeout = connectionWaitTimeout;
    }

    public void setONSConfiguration(String ONSConfiguration) {
        this.ONSConfiguration = ONSConfiguration;
    }

    public void setFastConnectionFailoverEnabled(boolean fastConnectionFailoverEnabled) {
        this.fastConnectionFailoverEnabled = fastConnectionFailoverEnabled;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setURL(String URL) {
        this.URL = URL;
    }

    public void setLoginTimeout(int loginTimeout) {
        this.loginTimeout = loginTimeout;
    }

    public void setReadTimeoutMillis(Long readTimeoutMillis) {
        this.readTimeoutMillis = readTimeoutMillis;
    }

    public void setMaxStatements(int maxStatements) {
        this.maxStatements = maxStatements;
    }

    @Bean
    @Primary
    @ConfigurationProperties("oracle-end-to-end-metric-data-source-wrapper")
    public DataSource dataSource() throws SQLException, UniversalConnectionPoolException {
        OracleEndToEndMetricDataSourceWrapper oracleEndToEndMetricDataSourceWrapper =
                new OracleEndToEndMetricDataSourceWrapper();
        oracleEndToEndMetricDataSourceWrapper.setTargetDataSource(targetDataSource());

        return oracleEndToEndMetricDataSourceWrapper;
    }

    @PreDestroy
    public void shutdown() throws UniversalConnectionPoolException {
        for ( String poolName : UniversalConnectionPoolManagerImpl
                .getUniversalConnectionPoolManager()
                .getConnectionPoolNames()) {
            UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager().destroyConnectionPool(poolName);
        }
    }

    @Bean
    public DataSource targetDataSource() throws SQLException, UniversalConnectionPoolException {
        shutdown(); //needed after 12.0.2 - bug or feature - nevertheless...

        PoolDataSource poolDataSource = PoolDataSourceFactory.getPoolDataSource();
        poolDataSource.setConnectionPoolName("JFRPOOL");
        poolDataSource.setConnectionFactoryClassName(OracleDataSource.class.getName());
        poolDataSource.setMinPoolSize(minPoolSize);
        poolDataSource.setMaxPoolSize(maxPoolSize);
        poolDataSource.setInitialPoolSize(initialPoolSize);
        poolDataSource.setValidateConnectionOnBorrow(validateConnectionOnBorrow);
        poolDataSource.setMaxConnectionReuseTime(maxConnectionReuseTimeSec);
        poolDataSource.setInactiveConnectionTimeout(inactiveConnectionTimeoutSec);
        poolDataSource.setAbandonedConnectionTimeout(abandonedConnectionTimeout);
        poolDataSource.setTimeToLiveConnectionTimeout(timeToLiveConnectionTimeout);
        poolDataSource.setConnectionWaitTimeout(connectionWaitTimeout);
        poolDataSource.setONSConfiguration(ONSConfiguration);
        poolDataSource.setFastConnectionFailoverEnabled(fastConnectionFailoverEnabled);
        poolDataSource.setMaxStatements(maxStatements);

        poolDataSource.setURL(URL);
        poolDataSource.setUser(user);
        poolDataSource.setPassword(password);
        poolDataSource.setLoginTimeout(loginTimeout);

        Properties properties = new Properties();
        properties.put(OracleConnection.CONNECTION_PROPERTY_THIN_READ_TIMEOUT, readTimeoutMillis.toString());
        properties.put("DMSStatementMetrics", Boolean.TRUE.toString());
        properties.put("oracle.jdbc.DMSStatementMetrics", Boolean.TRUE.toString());
        poolDataSource.setConnectionProperties(properties);

        return poolDataSource;
    }

    @Bean
    @Primary
    @ConfigurationProperties("transactionManager")
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory ) {
        JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
        jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);

        return jpaTransactionManager;
    }

    @Bean
    @ConfigurationProperties( "spring.jpa" )
    public JpaProperties jpaProperties() {
        return new JpaProperties();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean(DataSource dataSource, JpaProperties jpaProperties) {
        LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        localContainerEntityManagerFactoryBean.setDataSource(dataSource);

        HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
        hibernateJpaVendorAdapter.setDatabase(jpaProperties.getDatabase());
        hibernateJpaVendorAdapter.setGenerateDdl(jpaProperties.isGenerateDdl());
        hibernateJpaVendorAdapter.setShowSql(jpaProperties.isShowSql());
        hibernateJpaVendorAdapter.setDatabasePlatform(jpaProperties.getDatabasePlatform());

        Properties properties = new Properties();
        properties.putAll(jpaProperties.getProperties());

        localContainerEntityManagerFactoryBean.setJpaVendorAdapter(hibernateJpaVendorAdapter);
        localContainerEntityManagerFactoryBean.setJpaProperties(properties);

        return localContainerEntityManagerFactoryBean;
    }
}

I get this error:

testAbleToCreateWebAppContext(com.edb.fs.tac.jfr.srv.ws.ApplicationBootupSmokeTest)  Time elapsed: 0.001 s  <<< ERROR!
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dbUnitDatabaseConnection' defined in class path resource [com/edb/fs/tac/jfr/srv/test/springdbunit/DbUnitConfig.class]: Unsatisfied dependency expressed through method 'dbUnitDatabaseConnection' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [com/edb/fs/tac/jfr/srv/dao/infra/DataSourceConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'targetDataSource' defined in class path resource [com/edb/fs/tac/jfr/srv/dao/infra/DataSourceConfig.class]: 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?
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [com/edb/fs/tac/jfr/srv/dao/infra/DataSourceConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'targetDataSource' defined in class path resource [com/edb/fs/tac/jfr/srv/dao/infra/DataSourceConfig.class]: 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?
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'targetDataSource' defined in class path resource [com/edb/fs/tac/jfr/srv/dao/infra/DataSourceConfig.class]: 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?
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'targetDataSource' defined in class path resource [com/edb/fs/tac/jfr/srv/dao/infra/DataSourceConfig.class]: 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?
Caused by: 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?
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'dataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?

because DataSourceInitializer gets called, even though I have disabled any initialization of schemas with:

spring:
  autoconfigure:
    exclude: org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
  batch:
    initialize-schema: never
  datasource:
    continue-on-error: true
    initialization-mode: never

I think first of all the initializer should not kick in, as I have asked for never init mode. Secondary it should not fail when continue-on-error is true.

Also I think the code of DataSourceInitializerInvoker is too eager, because afterPropertiesSet -> getDataSourceInitializer, so that it attempts to access the datasource before onApplicationEvent(DataSourceSchemaCreatedEvent event) - and then the datasource is not ready.

A bit of a killer since:

  • it cannot be turned off
  • does not fail gracefully
  • has a bug/problematic code[path]
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Apr 14, 2018
@snicoll
Copy link
Member

snicoll commented Apr 16, 2018

That's another incantation of #9528 and a probably a dupe of #9394

@davidkarlsen can you please look at the workaround. If that doesn't work, please share a sample that we can run ourselves (code in comment is not very useful).

@snicoll snicoll added the status: waiting-for-feedback We need additional information before we can continue label Apr 16, 2018
@davidkarlsen
Copy link
Contributor Author

@snicoll Thanks for responding - in the meantime I have changed the whole thing into being a decorator via a BeanPostProcessor - so I do not have two visible DataSource beans in the context any longer - so I am not hit by this any longer.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Apr 16, 2018
@snicoll
Copy link
Member

snicoll commented Apr 16, 2018

Duplicate of #9394

@snicoll snicoll marked this as a duplicate of #9394 Apr 16, 2018
@snicoll snicoll closed this as completed Apr 16, 2018
@snicoll snicoll added status: duplicate A duplicate of another issue and removed status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged labels Apr 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: duplicate A duplicate of another issue
Projects
None yet
Development

No branches or pull requests

3 participants