Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring cacheable not synchronized

I have a singleton class (@Service annotated). This class has a method which takes 200/300ms to execute.

This method is annotated with @Cacheable and synchronized.

@Cacheable(value="nextPlaying", key = "#startingFrom.getYear() + #startingFrom.getMonth() + #startingFrom.getDay() + #startingFrom.getHours() + #startingFrom.getMinutes() + #locale.getLanguage()")
public synchronized List<Match> getNextPlaying(Date startingFrom, Locale locale)

By launching multiple threads calling this method I see that for these 200/300ms until the result isn't cached, it executes again and again the method until is cached. Seems that @Cacheable annotation doesn't take synchronized into account... Is this a bug?

like image 395
ianaz Avatar asked Mar 05 '15 16:03

ianaz


People also ask

What is sync in cacheable?

The sync parameter says: Synchronize the invocation of the underlying method if several threads are attempting to load a value for the same key.

What is cacheable spring?

Annotation indicating that the result of invoking a method (or all methods in a class) can be cached. Each time an advised method is invoked, caching behavior will be applied, checking whether the method has been already invoked for the given arguments.

How does @cacheable work in spring?

The @Cacheable annotation takes care of putting the result into the cache. After the first call, the cached value is in the cache and stays there according to the cache configuration. When the method is called the second time, and the cache value has not been evicted yet, Spring will search for the value by the key.

What is cacheable value?

The Cacheable annotation takes three arguments: value, which is mandatory, together with key and condition. The first of these, value, is used to specify the name of the cache (or caches) in which the a method's return value is stored.

Is there a way to synchronize a cache in spring?

Support for synchronized caches is added in Spring 4.3: spring.io/blog/2016/03/04/… Good news, spring framework 4.3 has provided a way to support your need, by adding sync=true in @Cacheable. here > docs.spring.io/spring-framework/docs/current/… also says your cache provider must support synchronized cache implementation...

How do I pass multiple caches as parameters in Spring Framework?

While in most cases one cache is enough, the Spring framework also supports multiple caches to be passed as parameters: @Cacheable ( {"addresses", "directory"}) public String getAddress(Customer customer) {...} In this case, if any of the caches contain the required result, the result is returned and the method is not invoked.

What is @cacheable annotation in Spring Framework?

Spring Framework has built-in classes and annotations to help developers dealing with caching. The @Cacheable annotation is one of them, it is the most widely used Spring cache-related annotations. In this article, we will go on the usage of this annotation and important keynotes about it.

How to enable caching in Spring Boot?

If we use Spring Boot, then we can utilize the spring-boot-starter-cache starter package to easily add the caching dependencies: Under the hood, the starter brings the spring-context-support module. 3. Enable Caching To enable caching, Spring makes good use of annotations, much like enabling any other configuration level feature in the framework.


Video Answer


2 Answers

Good news, spring framework 4.3 has provided a way to support your need, by adding sync=true in @Cacheable.

like image 156
Clement.Xu Avatar answered Sep 18 '22 13:09

Clement.Xu


When you use the @Cacheable annotation, the code that implements the cache search is outside of your method. Therefore, the synchronized modifier does not affect it.

If you want all the threads to use the cached result, you should create a synchronized method that wraps the invocation to the cacheable getNextPlaying method. Something like this:

public synchronized List<Match> getNextPlayingSynchronized(Date startingFrom, Locale locale){
     return getNextPlaying(Date startingFrom, Locale locale);
}
...
@Cacheable(value="nextPlaying", key = "#startingFrom.getYear() + #startingFrom.getMonth() + #startingFrom.getDay() + #startingFrom.getHours() + #startingFrom.getMinutes() + #locale.getLanguage()")
public List<Match> getNextPlaying(Date startingFrom, Locale locale){
...//your old method without the synchronized modifier
}

It's important that these methods are in different classes. Otherwise, the aspects don't work.

like image 25
Andres Avatar answered Sep 20 '22 13:09

Andres