15

I've enabled Caching in my Spring app and I use Redis to serve the purpose. However, whenever a connection failure occurs, the app stops working whereas I think it had better skip the Caching and go on with normal execution flow.

So, does anyone have any idea on how to gracefully do it in Spring ?

Here is the exception I got.

Caused by: org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
Peter Bean
  • 313
  • 2
  • 9

3 Answers3

8

As from Spring Framework 4.1, there is a CacheErrorHandler that you can implement to handle such exceptions. Refer to the javadoc for more details.

You can register it by having your @Configuration class extends CachingConfigurerSupport (see errorHandler()).

Stephane Nicoll
  • 31,977
  • 9
  • 97
  • 89
  • Thanks very much for your suggestion. My `@Configuration` class already extends SpringBootServletInitializer, so, I tried implementing CacheErrorHandler directly instead from my `@Configuration` class. However, it doesn't seem to handle anyting like RedisConnectionFailureException. – Peter Bean Jan 06 '15 at 06:34
  • Where have you seen a reference to `SpringBootServletInitializer` exactly? I wrote `CachingConfigurerSupport` - You need Spring Boot 1.2 (Spring 4.1) for this. – Stephane Nicoll Jan 06 '15 at 10:17
  • 1
    CacheErrorHandler is used only during cache operations. A Redis connection failure might be occurring higher up? – Jaymes Bearden Jan 30 '15 at 07:11
  • uh? What do you mean by higher up? – Stephane Nicoll Feb 04 '15 at 11:38
  • 1
    I think he propbably meant that when the connection is failed, the CacheErrorHandler may not be triggered – Peter Bean Sep 19 '15 at 08:52
  • Would be nice if after prevent the error some kind of circuit breaker mechanisc were triggered. Because if there are too many calls to the cached method, all responses would take at least the timeout of Redis connection plus the actual work to get the data. I was trying to find some way to do this but I did not have much success. – Biga Jun 27 '19 at 13:59
  • I did what @StephaneNicoll said, but earlier I was getting exception even before the method with marked with Cacheable annotation invoked, but now the method executes successfully and data is fetched from DB rather than Redis, but I get exception after the function has executed. This might be where the result is being put into cache and it can't get a connetion to Redis since it is down. Any solution to this ? – mantri Jul 11 '20 at 11:15
  • Not sure how CacheHandler hanldes Configuration exception. – Rocky4Ever Aug 04 '20 at 00:11
2

You can use CacheErrorHandler as suggested by Stephane Nicoll. But you should make sure to make RedisCacheManager transactionAware to false in your Redis Cache Config(to make sure the transaction is committed early when executing the caching part and the error is caught by CacheErrorHandler and don't wait until the end of the execution which skips CacheErrorHandler part). The function to set transactionAware to false looks like this:

    @Bean
    public RedisCacheManager redisCacheManager(LettuceConnectionFactory lettuceConnectionFactory) {
        JdkSerializationRedisSerializer redisSerializer = new JdkSerializationRedisSerializer(getClass().getClassLoader());

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(redisDataTTL))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer));

        redisCacheConfiguration.usePrefix();

        RedisCacheManager redisCacheManager = RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(lettuceConnectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .build();

        redisCacheManager.setTransactionAware(false);
        return redisCacheManager;
    }
Yashasvi Raj Pant
  • 1,274
  • 4
  • 13
  • 33
-1

Similar to what Stephane has mentioned, I have done in by consuming the error in try catch block. Adding a fall back mechanism where if Redis is not up or may be the data is not present then I fetch the data from DB.(Later if I find one then I add the same data in Redis,if it is up to maintain consistency.)