-
Notifications
You must be signed in to change notification settings - Fork 38.9k
Distinguish CacheErrorHandler#handleCachePutError based on trigger context #36531
Description
Background
I'm using @Cacheable and @CachePut annotations with Redis to implement caching. To support high availability, the service should return responses even when Redis failures occur.
This is my approach.
@Cacheable(...)
public Something mainLogic(...) {
// main logic
}
@CachePut(...)
public Something putLogic(...) {
// main logic
}
public class CustomCacheErrorHandler extends SimpleCacheErrorHandler {
@Override
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
// log and record metrics — error is swallowed intentionally
}
}Problem
Current flow when Redis is unavailable:
HTTP Request
→ Redis GET (failure)
→ handleCacheGetError ← error swallowed ✅
→ mainLogic() executes ← works as expected ✅
→ Redis PUT (failure)
→ handleCachePutError ← error NOT swallowed ❌
→ HTTP Request fails ← client sees error ❌
Overriding handleCacheGetError alone is insufficient. After mainLogic() succeeds, @Cacheable attempts to write the result back to Redis — and since handleCachePutError is not overridden, that error propagates to the client.
However, simply swallowing all errors in handleCachePutError is also problematic: it would silently suppress errors from @CachePut (ex putLogic()).
The core issue is on that handleCachePutError is invoked in two semantically distinct scenarios.
Idea
Option 1 — Add a reason parameter to distinguish the trigger context
handleCachePutError(
PutReason reason, // e.g. CACHEABLE_WRITE_BACK vs CACHE_PUT
RuntimeException exception,
Cache cache,
Object key,
Object value
);
Option 2 — Introduce a dedicated callback for @Cacheable write-back
handleCachePutErrorWriteBack(
RuntimeException exception,
Cache cache,
Object key,
Object value
);
It separates the two scenarios in interface level.