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.
Background
I'm using
@Cacheableand@CachePutannotations with Redis to implement caching. To support high availability, the service should return responses even when Redis failures occur.This is my approach.
Problem
Current flow when Redis is unavailable:
Overriding handleCacheGetError alone is insufficient. After mainLogic() succeeds,
@Cacheableattempts 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
Option 2 — Introduce a dedicated callback for
@Cacheablewrite-backIt separates the two scenarios in interface level.