springboot 2.7.0

upgrade to springboot 2.7.0 Project could not be started

Failed to start bean 'springSessionRedisMessageListenerContainer'; nested exception is java.lang.IllegalStateException: Subscription registration timeout exceeded.

Comment From: pruidong

Registration timeouts are generally misconfigurations. You can check your config file. Or provide a minimal example.

Comment From: leshalv

Looking at the update log, it should be caused by refactoring RedisMessageListenerContainer

Comment From: wilkinsona

@leshalv RedisMessageListenerContainer is part of Spring Data Redis which is managed as a separate project. Please open an issue with them. We can re-open this issue if it turns out that a change in Spring Boot is necessary to accommodate some changes in Spring Data Redis. If you open a Spring Data Redis issue, please take the time to provide a complete yet minimal sample that reproduces the problem.

Comment From: sylvermeister

Hello! I also experienced this by just upgrading the spring-boot-starter-parent to 2.7.0

org.springframework.context.ApplicationContextException: Failed to start bean 'springSessionRedisMessageListenerContainer'; nested exception is java.lang.IllegalStateException: Subscription registration timeout exceeded.
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181)
    at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54)
    at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356)
    at java.lang.Iterable.forEach(Iterable.java:75)
    at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155)
    at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123)
    at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
        at (omitted project)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
    at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65)
Caused by: java.lang.IllegalStateException: Subscription registration timeout exceeded.
    at org.springframework.data.redis.listener.RedisMessageListenerContainer.lazyListen(RedisMessageListenerContainer.java:274)
    at org.springframework.data.redis.listener.RedisMessageListenerContainer.start(RedisMessageListenerContainer.java:248)
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178)
    ... 22 common frames omitted
Caused by: java.util.concurrent.TimeoutException: null
    at java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1784)
    at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1928)
    at org.springframework.data.redis.listener.RedisMessageListenerContainer.lazyListen(RedisMessageListenerContainer.java:268)
    ... 24 common frames omitted

Comment From: wilkinsona

@sylvermeister Perhaps you can provide a minimal sample that both @mp911de and I have asked for. If you can, please comment on https://github.com/spring-projects/spring-session/issues/2098 attaching it or linking to it.

Comment From: mp911de

FWIW, when using the Lettuce driver, you can enable debug logging for the io.lettuce category to capture what Redis commands are issued and what responses you receive. With Spring Data Redis 2.7, we rewrote RedisMessageListenerContainer to await subscription confirmation from Redis to avoid race conditions.

Comment From: leshalv

Subscription registration timeout exceeded.

Comment From: rowi1de

FWIW, when using the Lettuce driver, you can enable debug logging for the io.lettuce category to capture what Redis commands are issued and what responses you receive. With Spring Data Redis 2.7, we rewrote RedisMessageListenerContainer to await subscription confirmation from Redis to avoid race conditions.

@mp911de could you give some more details about the rewrite? What does "await subscription confirmation" mean? I even increased to subscription timeout to crazy levels and it will always time out. Would be good to know what changes are required. I'm getting failures by just upgrading from 2.6.x to 2.7. EDIT: Found https://github.com/spring-projects/spring-data-commons/wiki/Spring-Data-2021.2-(Raj)-Release-Notes#revised-redismessagelistenercontainer

Comment From: AmanMoglix

Need to update things

<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.17.3</version>
        </dependency>
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.Jedis;

@Configuration
public class RedisConfig {

    @Autowired
    private ApplicationConfig applicationConfig;
    @Autowired
    @Lazy
    private UserCoinHistoryRepository userCoinHistoryRepository;
    @Autowired
    @Lazy
    private CoinTransactionFactory coinTransactionFactory;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    @Lazy
    private RedisTemplate < String, Object > redisTemplate;

    @Bean
    public RedisTemplate<String, Object> redisTemplateJedis(RedisConnectionFactory cf) {
        RedisTemplate<String, Object> redisTemplateJedis = new RedisTemplate<String, Object>();
        redisTemplateJedis.setConnectionFactory(cf);
        redisTemplateJedis.setKeySerializer(new StringRedisSerializer());
        redisTemplateJedis.setValueSerializer(new StringRedisSerializer());
        redisTemplateJedis.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
        return redisTemplateJedis;
    }


    @Bean
    public MessageListenerAdapter messageListenerAdapter() {
        return new MessageListenerAdapter(new RedisKeyExpirationListener(userCoinHistoryRepository,coinTransactionFactory,redissonClient,redisTemplate));
    }

    /**
     * Configures a RedisMessageListenerContainer bean that sets up a Redis message listener.
     * This container listens for keyspace notifications on all keys that match the pattern "__key*__:*".
     *
     * @param connectionFactory the factory to establish Redis connections
     * @param messageListenerAdapter the adapter that handles the messages received
     * @return the configured RedisMessageListenerContainer
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory,
                                                                       MessageListenerAdapter messageListenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(messageListenerAdapter, new PatternTopic("__key*__:*"));
        return container;
    }
    @Bean
    public Jedis jedisClient() {
        return new Jedis(applicationConfig.getRedisServer(),applicationConfig.getRedisPort());
    }
}