title: Spring-data-redis cache - Calls backend method even data is cached if I use CustomizeRedisCacheManager
body: <p>I am using Spring-data-redis cache  (1.6.1), Jredis Client 2.7.3.  We want to enabled failover. If the redis server is down, I want the app the application to keep on working</p>
  
  <p>1) If I used spring-data xml configuration, redis cache works fine.  It calls the backend, caches data and then does not call backend on second call.  However, I don't know how to capture the Redis Exception if the server is down using xml configuration and return null so that application keep on working.  Not enough documentation.  (This solution does not work)</p>
  
  <p>2) I have configured redis cache using Java,  In this case, I am able to catch failover excetpion, but Spring-data-redis, keep on calling the backend database method, even if the redis cache is working.  So it should call the backend method and cache data.  Second call should not go back to the backend database.</p>
  
  <p>So it is caching and calling backend database method on all the requests.  </p>
  
  <p>I was wondering if someone has faced this issue.  Or any idea on how to failover spring-data-redis if the backend Redis server is down.</p>
  
  <p>I have attached the sample code.</p>
  
  <p>Here is AppConfig.java</p>
  
  <pre><code>Configuration
  @EnableCaching
  @ComponentScan("com.mkyong.helloworld.service")
  @PropertySource("classpath:/redis.properties")
  public class AppConfig extends CachingConfigurerSupport{
      private @Value("${redis.host-name}") String redisHostName;
      private @Value("${redis.port}") int redisPort;
  //  private @Value("${cache.expire}") long cacheExpire;
  //  private @Value("${cache.name}") String cacheName; 
  
  
  private Log logger = LogFactory.getLog(getClass());
  
   @Bean
   public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
       return new PropertySourcesPlaceholderConfigurer();
   }
  
   @Bean
   JedisConnectionFactory jedisConnectionFactory() {
       JedisConnectionFactory factory = new JedisConnectionFactory();
       factory.setHostName(redisHostName);
       factory.setPort(redisPort);
       factory.setUsePool(true);
       return factory;
   }
  
   @Bean
   RedisTemplate&lt;Object, Object&gt; redisTemplate() {
       RedisTemplate&lt;Object, Object&gt; redisTemplate = new RedisTemplate&lt;Object, Object&gt;();
       redisTemplate.setConnectionFactory(jedisConnectionFactory());
       return redisTemplate;
   }
  
   @Bean
   public CacheManager cacheManager() {
  //     return new RedisCacheManager(redisTemplate());
  //      logger.debug("Calling Redis CustomRedisCacheManager()" );
  //     return new CustomRedisCacheManager(redisTemplate());
       logger.debug("START: operation=cacheManager");
       UUID transactionId = UUID.randomUUID();
       long startTime = System.currentTimeMillis();
       long stopTime=0;
       long elapsedTime=0;
       String userTokenCache1="descCache";
       String userTokenCache2="titleCache";
       //Long expiration = environment.getProperty("cache.default.timeout", Long.class);
       Long expiration = 360000L;
       Map&lt;String, Long&gt; expires = new ConcurrentHashMap&lt;&gt;(1);
       expires.put(userTokenCache1, expiration);
       expires.put(userTokenCache2, expiration);
       CustomRedisCacheManager cacheMgr = new CustomRedisCacheManager( redisTemplate() );
       cacheMgr.setExpires(expires);
  //   //cacheMgr.setDefaultExpiration(expires);
  //   cacheMgr.setCacheNames(Arrays.asList(userTokenCache));
       stopTime = System.currentTimeMillis();
       elapsedTime = stopTime - startTime;
       logger.debug("Cache Name = " + userTokenCache1 + " cacheExpire=" + userTokenCache1 );
       logger.debug("END: transcation_id=" + transactionId + " operation=cacheManager" + " status=Completed, execution_time=" + elapsedTime );
  
       return cacheMgr;
   }
  
  // @Bean // important!
  // @Override
  // public CacheErrorHandler errorHandler() {
  //     // configure and return CacheErrorHandler instance
  //   CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
  //      
  //      @Override
  //      public void handleCachePutError(RuntimeException exception, Cache cache,
  //              Object key, Object value) {
  //          // TODO Auto-generated method stub
  //          logger.warn("As the Redis Cache server may be down. Unable to save Cache..." );
  //      }
  //      
  //      @Override
  //      public void handleCacheGetError(RuntimeException exception, Cache cache,
  //              Object key) {
  //          // TODO Auto-generated method stub
  //          logger.warn("As the Redis Cache server may be down. Fetching Data from the backend..." );
  //      
  //      }
  //      
  //      @Override
  //      public void handleCacheEvictError(RuntimeException exception, Cache cache,
  //              Object key) {
  //          // TODO Auto-generated method stub
  //          logger.warn("As the Redis Cache server may be down. Unable to evict cache..." );
  //      }
  //      
  //      @Override
  //      public void handleCacheClearError(RuntimeException exception, Cache cache) {
  //          // TODO Auto-generated method stub
  //          logger.warn("As the Redis Cache server may be down. Unable to clear cache..." );
  //      }
  //  };
  //  return cacheErrorHandler;
  // }
  
  }
  </code></pre>
  
  <p>Here is HelloWorldServica.java</p>
  
  <pre><code>@Service
  public class HelloWorldService {
  
      private static final Logger logger = LoggerFactory.getLogger(HelloWorldService.class);
  
       @Cacheable(value="descCache")
      public String getDesc() {
  
          logger.debug("getDesc() is executed!");
  
          return "Gradle + Spring MVC Hello World Example";
  
      }
       @Cacheable(value="titleCache")
      public String getTitle(String name) {
  
          logger.debug("getTitle() is executed! $name : {}", name);
  
          if(StringUtils.isEmpty(name)){
              return "Hello World";
          }else{
              return "Hello " + name;
          }
  
      }
  
  }
  </code></pre>
  
  <p>WelcomeController.java</p>
  
  <pre><code>@Controller
  public class WelcomeController {
  
      private final Logger logger = LoggerFactory.getLogger(WelcomeController.class);
      private final HelloWorldService helloWorldService;
      ApplicationContext ctx = null;
  
     @Autowired
      public WelcomeController(HelloWorldService helloWorldService) {
          this.helloWorldService = helloWorldService;
          ctx = new AnnotationConfigApplicationContext(AppConfig.class);
  
     }
  
      @RequestMapping(value = "/", method = RequestMethod.GET)
      public String index(Map&lt;String, Object&gt; model) {
  
          logger.debug("index() is executed!");
  
          model.put("title", helloWorldService.getTitle(""));
          model.put("msg", helloWorldService.getDesc());
  
          return "index";
      }
  
      @RequestMapping(value = "/hello/{name:.+}", method = RequestMethod.GET)
      public ModelAndView hello(@PathVariable("name") String name) {
  
          logger.debug("hello() is executed - $name {}", name);
  
          ModelAndView model = new ModelAndView();
          model.setViewName("index");
  
          model.addObject("title", helloWorldService.getTitle(name));
          model.addObject("msg", helloWorldService.getDesc());
  
          return model;
  
      }
  
      class CustomRedisCacheManager extends RedisCacheManager {
      private static Log logger = LogFactory.getLog(CustomRedisCacheManager.class);
      public CustomRedisCacheManager(RedisTemplate redisTemplate) {
          super(redisTemplate);
      }
      @Override
      public Cache getCache(String name) {
          return new RedisCacheWrapper(super.getCache(name));
      }
  
      protected static class RedisCacheWrapper implements Cache {
          private final Cache delegate;
          public RedisCacheWrapper(Cache redisCache) {
              logger.debug("Start:RedisCacheWrapper()" );
              UUID transactionId = UUID.randomUUID();
              long startTime = System.currentTimeMillis();
              long stopTime=0;
              long elapsedTime=0;
              Assert.notNull(redisCache, "'delegate' must not be null");
              this.delegate = redisCache;
              stopTime = System.currentTimeMillis();
              elapsedTime = stopTime - startTime;
              logger.info(" transcation_id=" + transactionId + " operation=RedisCacheWrapper" + " status=Completed, execution_time (ms)=" + elapsedTime );
              logger.debug("End:RedisCacheWrapper()" );
  
          }
          @Override
          public Cache.ValueWrapper get(Object key) {
              logger.debug("As the Redis Cache server may be down. Unable to save Cache..." );
              try {
                  delegate.get(key);
              }
              catch (Exception e) {
                  try {
                      return handleErrors(e);
                  } catch (Exception e1) {
                      // TODO Auto-generated catch block
                      e1.printStackTrace();
                  }
              }
              return null;
          }
          @Override
          public void put(Object key, Object value) {
              try {
                  delegate.put(key, value);
              }
              catch (Exception e) {
                  try {
                      handleErrors(e);
                  } catch (Exception e1) {
                      // TODO Auto-generated catch block
                      e1.printStackTrace();
                  }
              }
          }
          @Override
          public String getName() {
              // TODO Auto-generated method stub
              return null;
          }
          @Override
          public Object getNativeCache() {
              // TODO Auto-generated method stub
              return null;
          }
          @Override
          public &lt;T&gt; T get(Object key, Class&lt;T&gt; type) {
              // TODO Auto-generated method stub
              return null;
          }
          @Override
          public ValueWrapper putIfAbsent(Object key, Object value) {
              // TODO Auto-generated method stub
              return null;
          }
          @Override
          public void evict(Object key) {
              // TODO Auto-generated method stub
  
          }
          @Override
          public void clear() {
              // TODO Auto-generated method stub
  
          }
  
          // implement clear(), evict(key), get(key, type), getName(), getNativeCache(), putIfAbsent(key, value) accordingly (delegating to the delegate).
          protected &lt;T&gt; T handleErrors(Exception e) throws Exception {
              UUID transactionId = UUID.randomUUID();
              long startTime = System.currentTimeMillis();
              long stopTime=0;
              long elapsedTime=0;
              logger.debug("Exception Thrown" + e.getMessage() );
              if (e instanceof RuntimeException ) 
              {
                  stopTime = System.currentTimeMillis();
                  elapsedTime = stopTime - startTime;
                  logger.info(" transcation_id=" + transactionId + " operation=Redis Cache" + " status=Redis cahce may be down, return null, cause=" + e.getMessage() + " execution_time=" + elapsedTime );
                  return null;
              } else {
                  throw e;
              }
  //          else if (&lt;something different&gt;) { // act appropriately }
  //              else {
  //                  throw e;
  //              }
  //          }
          }
  
      }
  </code></pre>