Search code examples
javaspringspring-bootspring-retry

spring boot retry connecting to database untill connection works


I'm trying to have a spring boot app retry to connect to a database when a connection fails and currently have this:

package backend.configuration.dbconnection;

import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.support.RetryTemplate;

import javax.sql.DataSource;

@Log
@Configuration
@EnableRetry
public class CustomDataSourceConfiguration {
    @Autowired
    private RetryTemplate retryTemplate;

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        //dataSource.setUrl("jdbc:mysql://localhost:3306/db");
        dataSource.setUrl("jdbc:mysql://localhost:3307/db");
        dataSource.setUsername("user");
        dataSource.setPassword("password");
        retryTemplate.execute(context -> {
            try {
                dataSource.getConnection();
                return dataSource;
            } catch (Exception e) {
                return null;
            }
        });
        return dataSource;
    }
}

and this:

package backend.configuration.dbconnection;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

@Configuration
@EnableRetry
public class RetryConfiguration {
    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(5);
        retryTemplate.setRetryPolicy(retryPolicy);
        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(1000);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        return retryTemplate;
    }
}

returning null upon not being able to connect, so that the retry will kick in. This seems to not be the case. How can I fix this?


Solution

  • if you are using spring-boot 3.0.0 / spring-framkework6.0.0 or higher , you can try to use the retry tools in reactor which is a non-blocking network framework.

    the code snippet is like following

    
       @Bean
        public DataSource dataSource() {
             DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
            //dataSource.setUrl("jdbc:mysql://localhost:3306/db");
            dataSource.setUrl("jdbc:mysql://localhost:3307/db");
            dataSource.setUsername("user");
            dataSource.setPassword("password");
    
            var connectionIsClosed = Mono.just(dataSource)
                    .map(e -> {
                        Connection connection = null;
                        try {
                            connection = dataSource.getConnection();
                            var closed = connection.isClosed();
                            if (!closed) {
                                return connection;
                            }
                            throw new RuntimeException("connection is closed");
                        } catch (SQLException ex) {
                            throw new RuntimeException(ex);
                        }
                    })
                    .retryWhen(Retry.backoff(3, Duration.ofSeconds(1)) // retry 3 times, and backoff 1 second
                            .maxBackoff(Duration.ofSeconds(5))  // max backoff time
                            .filter(throwable -> throwable instanceof RuntimeException)  // it only retry when the exception is RuntimeException
                            .onRetryExhaustedThrow((spec, rs) -> new ConnectException("remote server is invalid !")) // if retry 3 times and still failed, then throw ConnectException
                            .doBeforeRetry(retrySignal -> System.out.println("Retrying... connecting to database..."))) // print log before retry
                    .block();
    return dataSource;