Search code examples
springspring-bootspring-data-redisjedis

Updating to Spring Boot 3.0.6 + Jedis fails unit tests with JedisClusterOperationException


We have a spring boot application using jedis for connecting to redis and we use jedis for unit tests. Recently, due to a vulnerability, we updated to spring boot 3.0.6 + java 17 and all unit tests using jedis fail, we use spring-boot-starter-data-redis with jedis for unit tests. Be aware that downgrading back to spring boot 2.x.x (spring-boot-starter-data-redis gets downgraded to 2.x.x too) all unit tests work as expected.

I have shortened the stack thrown at failure, because the redis config and redis method is written in a our own library that we re-use across multiple apps:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'getClusterRedisConnectionFactory' defined in class path resource [com/sap/etd/cloud/commons/config/redis/RedisConfig.class]: Could not initialize cluster slots cache.
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1770) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:598) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:225) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1310) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1271) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:484) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:339) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1150) ~[spring-context-6.0.8.jar:6.0.8]
        at com.sap.etd.cloud.commons.config.redis.RedisConfig.getRedisTemplate(RedisConfig.java:71) ~[streaming-commons-3.8.11.jar:3.8.11]
        at com.sap.etd.cloud.commons.config.redis.RedisConfig$$SpringCGLIB$$0.CGLIB$getRedisTemplate$1(<generated>) ~[streaming-commons-3.8.11.jar:3.8.11]
        at com.sap.etd.cloud.commons.config.redis.RedisConfig$$SpringCGLIB$$2.invoke(<generated>) ~[streaming-commons-3.8.11.jar:3.8.11]
        at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) ~[spring-core-6.0.8.jar:6.0.8]
        at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-6.0.8.jar:6.0.8]
        at com.sap.etd.cloud.commons.config.redis.RedisConfig$$SpringCGLIB$$0.getRedisTemplate(<generated>) ~[streaming-commons-3.8.11.jar:3.8.11]
        at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[?:?]
        at java.lang.reflect.Method.invoke(Method.java:577) ~[?:?]
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:139) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:655) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:493) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1332) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1162) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1417) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:245) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1189) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1417) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:245) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1189) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1417) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) ~[spring-beans-6.0.8.jar:6.0.8]
        ... 90 more
Caused by: redis.clients.jedis.exceptions.JedisClusterOperationException: Could not initialize cluster slots cache.
        at redis.clients.jedis.providers.ClusterConnectionProvider.initializeSlotsCache(ClusterConnectionProvider.java:56) ~[jedis-5.0.0-alpha1.jar:?]
        at redis.clients.jedis.providers.ClusterConnectionProvider.<init>(ClusterConnectionProvider.java:32) ~[jedis-5.0.0-alpha1.jar:?]
        at redis.clients.jedis.UnifiedJedis.<init>(UnifiedJedis.java:130) ~[jedis-5.0.0-alpha1.jar:?]
        at redis.clients.jedis.JedisCluster.<init>(JedisCluster.java:179) ~[jedis-5.0.0-alpha1.jar:?]
        at redis.clients.jedis.JedisCluster.<init>(JedisCluster.java:172) ~[jedis-5.0.0-alpha1.jar:?]
        at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.createCluster(JedisConnectionFactory.java:413) ~[spring-data-redis-3.0.5.jar:3.0.5]
        at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.createCluster(JedisConnectionFactory.java:377) ~[spring-data-redis-3.0.5.jar:3.0.5]
        at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.afterPropertiesSet(JedisConnectionFactory.java:300) ~[spring-data-redis-3.0.5.jar:3.0.5]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1816) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1766) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:598) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:225) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1310) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1271) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:484) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:339) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332) ~[spring-beans-6.0.8.jar:6.0.8]
        at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1150) ~[spring-context-6.0.8.jar:6.0.8]
        at com.sap.etd.cloud.commons.config.redis.RedisConfig.getRedisTemplate(RedisConfig.java:71) ~[streaming-commons-3.8.11.jar:3.8.11]
        at com.sap.etd.cloud.commons.config.redis.RedisConfig$$SpringCGLIB$$0.CGLIB$getRedisTemplate$1(<generated>) ~[streaming-commons-3.8.11.jar:3.8.11]
        at com.sap.etd.cloud.commons.config.redis.RedisConfig$$SpringCGLIB$$2.invoke(<generated>) ~[streaming-commons-3.8.11.jar:3.8.11]
        at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) ~[spring-core-6.0.8.jar:6.0.8]
        at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-6.0.8.jar:6.0.8]
        at com.sap.etd.cloud.commons.config.redis.RedisConfig$$SpringCGLIB$$0.getRedisTemplate(<generated>) ~[streaming-commons-3.8.11.jar:3.8.11]
        at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[?:?]

This is our RedisConfig class file


@Configuration
@EnableAsync
@Log4j2
public class RedisConfig {

    private final ApplicationContext context;
    
    private final CfEnvUtils cfEnvUtils;
    
    @Autowired
    public RedisConfig(ApplicationContext context, CfEnvUtils cfEnvUtils) {
        this.context = context;
        this.cfEnvUtils = cfEnvUtils;
    }
    
    
    private JedisClientConfiguration getJedisClientConfiguration() {
        log.info("Preparing Jedis Client Config for ");
        return JedisClientConfiguration.builder().useSsl().build();
    }
    
    
    @Bean
    public RedisConnectionFactory getClusterRedisConnectionFactory() {
        if (cfEnvUtils.isInCloud()) {
            CfEnv cfEnv = new CfEnv();
            String redisHost = cfEnv.findCredentialsByTag(REDIS_TAG).getHost();
            String redisPort = cfEnv.findCredentialsByTag(REDIS_TAG).getPort();
            String redisPassword = cfEnv.findCredentialsByTag(REDIS_TAG).getPassword();
            JedisConnectionFactory jcf;
            JedisClientConfiguration clientConfig = getJedisClientConfiguration();
            RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration(
                    Collections.singletonList(redisHost + ":" + redisPort));
            clusterConfiguration.setPassword(redisPassword);
            jcf = new JedisConnectionFactory(clusterConfiguration, clientConfig);
            return jcf;
        } else {
            JedisClientConfiguration clientConfig = getJedisClientConfiguration();
            RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration(
                    Collections.singletonList("localhost:" + 6379));
            JedisConnectionFactory jcf;
            jcf = new JedisConnectionFactory(clusterConfiguration, clientConfig);
            return jcf;
        }
        
    }
    
    
    @Bean
    public StringRedisTemplate getRedisTemplate() {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(context.getBean(RedisConnectionFactory.class));
        stringRedisTemplate.setEnableTransactionSupport(true);
        return stringRedisTemplate;
    }

}

Notice that the main error is redis.clients.jedis.exceptions.JedisClusterOperationException: Could not initialize cluster slots cache. which refers to the initialisation of the jedis on localhost:6379. I am assuming that spring-boot 3.0.6 is not prepared for jedis 4.3.2 or I am missing something that has changed in the configurations.

I have tried different jedis versions and spring-boot to make it work but I am missing the root cause.

You can find the pom.xml file here

You can find the output of mvn dependency:tree here.

UPDATE Debugging JedisConnectionException resulted to: "Failed to connect to any host resolved for DNS name." Debug Jedis


Solution

  • I wonder what is in your unit tests. Could it be just limited to creating a cluster connection object? Without executing any other command?

    Why I'm wondering this because of a certain difference between Jedis 3.x and Jedis 4+ which seems to matter in your case.
    In Jedis 3.x, when a JedisCluster object was being created with all cluster nodes down/unavailable, the constructor would not throw any exception and the object would be created BUT it would be a stale object with which no server operations can be made.
    Jedis 4.0 onwards started throwing exception in this case with message Could not initialize cluster slots cache..