Is there a way to prevent spring-boot application from failing on startup due to external connection failures? I have found other similar questions that suggest using @Lazy annotation to prevent @Configuration beans initialisation but this solution did not work for me with Spring Data Redis using Jedis client.
Also, other solutions like this one are specific to dependencies being used in the app. For example, Spring Cloud has below property to control failfast behaviour -
spring.cloud.config.fail-fast=true
You can use this project that I created for my problem to reproduce by shutting down the redis server.
Below is how my code looks like -
@Lazy
@Configuration
public class RedisConfiguration {
@Value("${spring.redis.sentinel.master}")
private String SENTINEL_MASTER;
@Value("${spring.redis.sentinel.nodes}")
private String SENTINEL_NODES;
@Value("${spring.redis.security.enabled:false}")
private boolean REDIS_SECURITY_ENABLED;
@Value("${spring.redis.security.password:}")
private String REDIS_PASSWORD;
@Lazy
@Bean // somehow this always gets initialized
public RedisConnectionFactory jedisConnectionFactory() {
// create set of sentinel nodes
System.out.println(SENTINEL_NODES);
Set<String> sentinelNodesSet = new HashSet<>(5);
StringTokenizer st = new StringTokenizer(SENTINEL_NODES, ",");
while (st.hasMoreTokens())
sentinelNodesSet.add(st.nextToken());
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration(SENTINEL_MASTER, sentinelNodesSet);
if (REDIS_SECURITY_ENABLED) {
sentinelConfig.setPassword(REDIS_PASSWORD);
}
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(sentinelConfig);
return jedisConnectionFactory;
}
Below is the exception trace -
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'stringRedisTemplate' defined in class path resource [org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.class]: Unsatisfied dependency expressed through method 'stringRedisTemplate' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jedisConnectionFactory' defined in class path resource [com/springboot/redisintegration/RedisConfiguration.class]: Invocation of init method failed; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: All sentinels down, cannot determine where is mysentinel master is running... at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:797) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:538) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1176) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:897) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879) ~[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551) ~[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE] at com.springboot.redisintegration.RedisIntegrationApplication.main(RedisIntegrationApplication.java:21) ~[classes/:na] Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jedisConnectionFactory' defined in class path resource [com/springboot/redisintegration/RedisConfiguration.class]: Invocation of init method failed; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: All sentinels down, cannot determine where is mysentinel master is running... at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1794) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1307) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:884) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:788) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] ... 20 common frames omitted Caused by: redis.clients.jedis.exceptions.JedisConnectionException: All sentinels down, cannot determine where is mysentinel master is running... at redis.clients.jedis.JedisSentinelPool.initSentinels(JedisSentinelPool.java:249) ~[jedis-3.3.0.jar:na] at redis.clients.jedis.JedisSentinelPool.(JedisSentinelPool.java:154) ~[jedis-3.3.0.jar:na] at redis.clients.jedis.JedisSentinelPool.(JedisSentinelPool.java:122) ~[jedis-3.3.0.jar:na] at redis.clients.jedis.JedisSentinelPool.(JedisSentinelPool.java:116) ~[jedis-3.3.0.jar:na] at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.createRedisSentinelPool(JedisConnectionFactory.java:374) ~[spring-data-redis-2.3.3.RELEASE.jar:2.3.3.RELEASE] at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.createPool(JedisConnectionFactory.java:358) ~[spring-data-redis-2.3.3.RELEASE.jar:2.3.3.RELEASE] at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.afterPropertiesSet(JedisConnectionFactory.java:342) ~[spring-data-redis-2.3.3.RELEASE.jar:2.3.3.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1790) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] ... 31 common frames omitted
In short:
@Lazyannotation works forRedisStandaloneConfigurationbut notRedisSentinelConfiguration, not sure why?- Using
@Lazyannotation is risky because you need to make sure all your services which are usingRedisare loaded lazily too. - Looking for a solution like
spring.cloud.config.fail-fast=trueprovided for spring cloud.
Update:
I've created below Jira issue for this feature -