Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@CachePut always prevents @Cacheable even with mutually exclusive conditions [SPR-11955] #16571

Closed
spring-projects-issues opened this issue Jul 4, 2014 · 5 comments
Assignees
Labels
in: core type: bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Jul 4, 2014

Andrea Polci opened SPR-11955 and commented

Before Spring 4.0 it was possible to use both @CachePut and @Cacheable annotation when they have a mutually exclusive condition.
This was also explicited in the reference documentation:

Note that using @CachePut and @Cacheable annotations on the same method is generally discouraged because they have different behaviors. While the latter causes the method execution to be skipped by using the cache, the former forces the execution in order to execute a cache update. This leads to unexpected behavior and with the exception of specific corner-cases (such as annotations having conditions that exclude them from each other), such declarations should be avoided.

This is an example of such use case:

@Cacheable(value = "cache", condition = "#useCache", key = "#root.args[0]")
@CachePut(value = "cache", condition = "!#useCache", key = "#root.args[0]")
public User findUserByUID(String userId, boolean useCache)

With spring 4.0 this is no more possible. The offending commit is eea230f that introduced the following change:

-		if(cachePutRequests.isEmpty()) {
 +		if(cachePutRequests.isEmpty() && contexts.get(CachePutOperation.class).isEmpty()) {
 			result = findCachedResult(contexts.get(CacheableOperation.class));
  		}

Affects: 4.0 GA, 4.0.5

Issue Links:

  • #15292 Make #result available for SpEL in @CachePut key attribute

Referenced from: commits e20ac27

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jul 4, 2014

Andrea Polci commented

Changed the Priority to Major, since a workaround is possible (by using two methods), but I think this is not a Minor issue since can have a major performance impact on previously working applications (this is my case).

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jul 5, 2014

Stéphane Nicoll commented

Adding the result attribute makes it hard to track if the condition passes until this result is available. Because we are now deferring the evaluation of the condition, any cache put is taken into account to figure out if the method has to be invoked (regardless of that condition).

That being said, using a cache should be transparent and your application should work exactly the same way with or without the cache. Why are you exposing those notions in your API? Can you elaborate a bit more on your use case? Thanks.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jul 5, 2014

Andrea Polci commented

The data cached by our method can change outside of our application control. Usually is perfectly fine to use an outdated value, but at specific points we need to ensure that we have the new data (and to update the cache at those points).
Our program works perfectly well (outside of performance issue) without the cache, but to obtain this we had to "mark" the calls that need to have fresh data, and those that can use old data.

We could do this by using two methods, one with the @Cacheable annotation and one with the @Cache put. We will use this now that we are upgrading to Spring 4.0, but with Spring 3.x this method worked and was mentioned in the documentation so we used it.

If I understand the problem is that since @CachePut can use #result in the condition, it's not possible to evaluate it without calling the real method.
What if we add a limitation? If you have both @Cacheable and @CachePut on a method you cannot use #result in the condition.
This never worked so there isn't a compatibility problem and it allow to evaluate the @CachePut condition before calling the method if we have a @Cacheable annotation.

If this is not possible I think the following action should be taken:

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jul 7, 2014

Stéphane Nicoll commented

This has been resolved on master in e20ac27fb

As you have suspected yourself, we can validate the @CachePut condition upfront if it does not use the result variable. When this is the case (as in your example), the caching behaviour works now as expected. I have refreshed the documentation to mention that point.

On a personal note, I think that calling a different method (with an explicit name) is better than calling the same method with a different flag as an argument. That being said, this used to work and this capability has now been restored on master and will be backported soon.

Thanks for the report!

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jul 7, 2014

Juergen Hoeller commented

This was a more or less intentional change in the 4.0 line, but point taken, we are re-enabling that capability in the 4.1 line now based on special tracking of access to the result variable.

Juergen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core type: bug
Projects
None yet
Development

No branches or pull requests

2 participants