In some cases, there is a need to check validity of the cached result each time to decide whether or not to generate a new value or proceed with the cached value. In such situations, using @Cacheable becomes impractical, nor @CacheEvict with @Scheduled can help. Since, unless parameter of the @Cacheable annotation is not evaluated once the result is cached, we can't use it for this purpose either.

As an idea, we could benefit from a new parameter on the annotation saying unlessWithCacheHit=true which could be false by default and make the same unless expression evaluate on each cache hit. Once the condition fails, we call the annotated method for a "fresh" result. What do you think?

Also, it would be very useful when used with external cache storages like Redis, when only one instance of the server that first encounters invalid (or about-to-expire) cache result updates the value, while others continue using the cached value, although implementation of this might need a different course of actions than of just adding the suggested annotation parameter.

Comment From: snicoll

@tbadalov unfortunately, that's out of scope of the cache abstraction. What you're trying to effectively do is implement a time-to-live of some sort for cache entries, evicting them automatically when they should no longer be present. The cache abstraction in the core framework is not an abstraction over cache libraries that have that feature, see https://docs.spring.io/spring-framework/reference/integration/cache/specific-config.html

Comment From: tbadalov

@snicoll, thanks for your comment. May I ask if this topic open for a discussion?

The cache abstraction in the core framework is not an abstraction

I have seen this argument all over the internet whenever a slight customization was needed while alternatives feel like a hack even if they somehow achieve the goal at all:

  1. Can I set a TTL for @Cacheable
  2. Expiry time @cacheable spring boot
  3. Someone asking the same question but hits the wall due to limitations: Spring cache value not being renewed when unless condition is not met

It gives an impression that even if all limitations stem from the fact that @Cacheable is an abstraction, then it feels out-of-place or not so useful for real needs if not a wrong abstraction. Perhaps we can improve the current situation besides using this excuse? After all, @Cacheable provides such useful configs like condition, unless it even allows to sync method invocations. None of those is what couldn't be handled by the actual caching provider. Yet, @Cacheable provides such options for us. Even if the framework doesn't handle it, should we raise the bar for providers? The annotation could provide a convenient interface to provide necessary parameters to the provider and if the provider supports that, it will do what is supposed to be done by the parameter.

Comment From: snicoll

Every topic is open to discussion, but as you can see by yourself you haven't provided any new argument. It's rather interesting you are providing a bunch of links where the situation is correctly explained.

I have seen this argument all over the internet

It's not an argument, it's a design decision.

It gives an impression that even if all limitations stem from the fact that @Cacheable is an abstraction, then it feels out-of-place or not so useful for real needs if not a wrong abstraction

Sure, if you conflate cache usage abstraction and cache configuration abstraction you'll feel that way. But our support is limited to the former and asking for support for the latter will inevitably end up in this situation. I don't know if you've realized but "using this excuse" is a bit rude IMO. I've tried to explain the scope of what we have and we're the ones ultimately making this call as we have to deal with maintenance and evolution.

Back to the feature at hand, you'll be better served by doing this in the cache library itself. It is its job to make sure things that shouldn't stay in the cache on a timely basis are evicted. If the cache library does not offer what you need, perhaps you should be using another cache? Trying to move the needle to the wrong abstraction will not help you.

Comment From: tbadalov

by doing this in the cache library itself

Any reason why params like condition, unless and sync are considered a fit for the spring framework? For sure these features could have been handled by the cache library itself

Comment From: bclozel

They were reasonably straightforward and high level enough that they could be considered at that level. Even sync is already stretching things a bit with a warning about cache implementations. The tradeoff still worth it considering the cases where expensive values would be resolved multiple times.

Here your request is making a case for TTL support, serving stale values, etc. Promoting this at a high level is different here since the behavior difference between implementations would be wider, we would need to implement custom solutions for some of them, and many other similar features would also qualify.

In that case, the Cacheable contract would quickly replicate the configuration of many cache implementations and would become confusing to many.