3

I'm connecting to an Azure SQL database and my next task is to create custom retry logic when a connection has failed. I would like the retry logic to run both on startup (if needed) as well as any time there's a connection failure while the app is running. I did a test where I removed the IP restrictions from my app and that then caused an exception in my application (as excepted). I'd like to handle when that exception is thrown so that I can trigger a job that verifies both the app and the server are configured correctly. I'm looking for a solution where I can handle these exceptions and retry the DB transaction?

DataSource Config

@Bean
@Primary
public DataSource dataSource() { 
     return DataSourceBuilder
            .create()
            .username("username")
            .password("password")
            .url("jdbc:sqlserver://contoso.database.windows.net:1433;database=*********;user=******@*******;password=*****;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;")
            .driverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver")
            .build();
}

application.properties

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServerDialect
spring.jpa.show-sql=true
logging.level.org.springframework.web: ERROR
logging.level.org.hibernate: ERROR
spring.datasource.tomcat.max-wait=10000
spring.datasource.tomcat.max-active=1
spring.datasource.tomcat.test-on-borrow=true
spring.jpa.hibernate.ddl-auto=update
Jim
  • 373
  • 5
  • 18

2 Answers2

5

The following code may help you create your retry logic for a data source on Spring Boot:

package com.example.demo;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.datasource.AbstractDataSource;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Retryable;

@SpringBootApplication
@EnableRetry
public class DemoApplication {

    @Order(Ordered.HIGHEST_PRECEDENCE)
    private class RetryableDataSourceBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName)
                throws BeansException {
            if (bean instanceof DataSource) {
                bean = new RetryableDataSource((DataSource)bean);
            }
            return bean;
        }

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName)
                throws BeansException {
            return bean;
        }
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    public BeanPostProcessor dataSouceWrapper() {
        return new RetryableDataSourceBeanPostProcessor();
    }
}

class RetryableDataSource extends AbstractDataSource {

    private DataSource delegate;

    public RetryableDataSource(DataSource delegate) {
        this.delegate = delegate;
    }

    @Override
    @Retryable(maxAttempts=10, backoff=@Backoff(multiplier=2.3, maxDelay=30000))
    public Connection getConnection() throws SQLException {
        return delegate.getConnection();
    }

    @Override
    @Retryable(maxAttempts=10, backoff=@Backoff(multiplier=2.3, maxDelay=30000))
    public Connection getConnection(String username, String password)
            throws SQLException {
        return delegate.getConnection(username, password);
    }

}
Alberto Morillo
  • 13,893
  • 2
  • 24
  • 30
0

Not sure what you deem custom but, there is an out-of-the-box option with Spring boot and Aspectj by leveraging the pom.xml in the mavern project, as spring-retry depends on Aspectj:

Add the following dependencies in your project pom.xml:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>${version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${version}</version>
</dependency>

And then add the @EnableRetry annotation to your code:

package com.example.springretry;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;
 
@EnableRetry
@SpringBootApplication
public class SpringRetryApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(SpringRetryApplication.class, args);
    }
}

The Spring retry module example can be found here: https://howtodoinjava.com/spring-boot2/spring-retry-module/

Mike Ubezzi
  • 1,007
  • 6
  • 8