'Spring Cache Error Handler - Send a Response back on CacheEvict Error

I am working on a fallback procedure for when the connection fail (or another error) occurs. I've created the CacheConfiguration/CacheErrorHandler to handle the errors and log them. The application successfully switches between using the cache and going through the normal process when Redis fails.

However, the way I've implemented cache eviction endpoint (via the @cacheEvict annotation), it is essentially an empty method.

@DeleteMapping(value = "/cache/clear")
@CacheEvict(value = {_values_}, allEntries = true)     
public ResponseEntity<String> clearAllCache() {return ResponseEntity.ok("OK"); }

Current CacheErrorHandler

@Override
public CacheErrorHandler errorHandler() {
    return new CacheErrorHandler() {
        @Override
    public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
        LOGGER.warn("Failure evicting from cache: " + cache.getName() + ", exception: " + exception);
        }
}

Logger will output the cacheEvictError but the response will send back "OK" to the client. Is there a way to catch the cache error and send a different response saying that the cache evict failed?

I've tried adding a try-catch to throw an exception inside the endpoint but that went nowhere. Couldn't seem to find any examples online to solve this specific issue.



Solution 1:[1]

One thing to keep in mind here is that Spring's @CacheEvict annotation and behavior is called "after" the method (by default) on which the annotation is declared, which in your case is the clearAllCache() Web service method.

Although, you can configure the cache eviction to occur before the (actual) clearAllCache() Web service method is called, like so:

@CacheEvict(cacheNames = { ... }, allEntries = true, beforeInvocation = true)
public ResponseEntity<String> clearAllCache() {
  // ...
}

That is, using the beforeInvocation attribute on the @CacheEvict annotation, set to true, the cache eviction (for all entries) will occur before the actual clearAllCache() method is invoked.

NOTE: Logically, if the invocation happens after the clearAllCache() method has already been called, then you really have no way to respond if the cache eviction (or rather, the "clear" operation) was unsuccessful. So you must configure the cache eviction to occur before your Web service method gets invoked, first of all.

Next, you need someway to know that your custom CacheErrorHandler was invoked on an error occurring in your caching provider (e.g. Redis) during eviction (or technically, the Cache.clear() operation in this case, since you evicting "all entries").

Another thing to keep in mind here is that since you appear to be operating in Web environment (e.g. a Servlet container like Tomcat or Jetty, or other) then you need to keep "Thread Safety" in mind since each HTTP request and corresponding Web handler method, like the clearAllCache() method called on HTTP DELETE, will be invoked from a separate Thread (i.e. Thread per (HTTP) Request model).

So, you can solve that problem using a Java ThreadLocal declared inside your custom CacheErrorHandler class to capture the necessary state / information that is needed once the clearAllCache() method is called.

I have wrote one such example test class demonstrating how you could accomplish this. The key to this implementation (solution) is the proper configuration of the cache eviction and the use of the ThreadLocal in the custom CacheErrorHandler.

My test is not specifically configured as a Web-based service (e.g. using Spring Web MVC, or anything like that), but I modeled the test use case after your particular situation. I also made use of Mockito to spy on the Spring caching infrastructure to always throw a RuntimeException anytime a Cache eviction based operation occurs (e.g. evict(key) or clear(), etc).

Of course, there are probably better, more robust ways to implement this solution, but this at least demonstrates that it is possible.

Hopefully, this gives you more ideas.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1