Let it boil a little. Your application uses caching (implemented using Redis). If the Redis connection is out of date / closed or otherwise, you want the application to bypass caching and (presumably) go directly to the underlying data store (e.g. RDBMS). The logic of a Service application might look just like ...
@Service class CustomerService ... { @Autowired private CustomerRepository customerRepo; protected CustomerRepository getCustomerRepo() { Assert.notNull(customerRepo, "The CustomerRepository was not initialized!"); return customerRepo; } @Cacheable(value = "Customers") public Customer getCustomer(Long customerId) { return getCustomerRepo().load(customerId); } ... }
All that matters in the Spring Caching Abstraction kernel to determine a cache miss is that the return value is null. Thus, the Spring Caching Infrastructure will act when the actual method of the service is called (i.e. GetCustomer). Keep in mind that when you return the call getCustomerRepo (). Load (customerId) You also need to handle the case where Spring Caching infrastructure is now trying to cache the value.
In the spirit of maintaining simplicity, we can do without AOP, but you can also achieve this using AOP (of your choice).
All you need (should) is a “customizable” RedisCacheManager that extends the SDR CacheManager implementation , something like ...
package example; import org.springframework.cache.Cache; import org.springframework.data.redis.cache.RedisCacheManager; ... class MyCustomRedisCacheManager extends RedisCacheManager { public MyCustomerRedisCacheManager(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) { Assert.notNull(redisCache, "'delegate' must not be null"); this.delegate = redisCache; } @Override public Cache.ValueWrapper get(Object key) { try { delegate.get(key); } catch (Exception e) { return handleErrors(e); } } @Override public void put(Object key, Object value) { try { delegate.put(key, value); } catch (Exception e) { handleErrors(e); } }
So, if Redis is unavailable, perhaps the best thing you can do is to register the problem and go to the service call. Obviously, this will hinder performance, but at least it will increase awareness of the existence of the problem. Obviously, this may be due to a more reliable notification system, but this is a rough example of the possibilities. The important thing is that your Service remains available, while other services (such as Redis), on which the application service depends, may have failed.
In this implementation (compared to my previous explanation), I decided to delegate the base, actual implementation of RedisCache to throw an Exception, and then I know well that the problem with Redis exists and that you can handle the Exception correctly, However, if you are sure that the exception is due to the connection problem during verification, you can return "null" so that the Spring Caching Infrastructure continues as if it were a "Error" error (i.e., skip the bad Redis Connection == Cache, in this case).
I know that something like this should help your problem, because I built a similar prototype of the “custom” implementation of CacheManager for GemFire and one of the Pivotal clients. In this particular UC, the “skip” cache should have been called by the “outdated version” of the application domain object, where in production there was a combination of older and older applications connecting to GemFire via Spring Caching Abstraction. For example, the fields of the application domain objects will change in new versions of the application.
Anyway, hope this helps or gives you more ideas.
Hooray!