Skip to content

Distinguish CacheErrorHandler#handleCachePutError based on trigger context #36531

@auto-jazzjack

Description

@auto-jazzjack

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.

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)status: waiting-for-triageAn issue we've not yet triaged or decided on

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions