Reputation: 177
I am using Spring 4.0.3, and utilizing the Cache Abstraction feature, backed by EhCache 2.8.1.
I was concerned that the method-level annotations @Cacheable
and @CacheEvict
may not properly lock the cache while editing it, resulting in thread safety problems. My test below seems to confirm that. My question is, am I misusing the framework or misinterpreting the results of my test? Or am I correct in concluding that the annotations do not cause the cache to be properly locked, and therefore the use of @CacheEvict
does not guarantee that future reads will be valid?
Test:
Create a database table to store a counter value
create table counter
(
counter integer
);
insert into counter values(0);
Create an entry in ehcache.xml to cache the counter value
<cache name="counter"
eternal="true"
maxElementsInMemory="1"/>
Create two request mapping methods in a Spring controller -- one to read the value of the counter and return after a long delay, and the other to increment the value and then return quickly
@RequestMapping("/viewcounter")
@ResponseBody
@Cacheable(value = "counter", key = "1")
public int readCounter() {
int count = dao.selectInteger("select counter from counter");
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
return count;
}
@RequestMapping("/incrementcounter")
@ResponseBody
@CacheEvict(value = "counter", key = "1")
public int incrementCounter() {
dao.update("update counter set counter = counter + 1");
int count = dao.selectInteger("select counter from counter");
return count;
}
Follow these three steps under two different scenarios -- first with the cache annotations removed from the controller methods, and then with them added back in
/incrementcounter
/viewcounter
, and then immediately after initiating this request, visit /incrementcounter
in another tab /viewcounter
Test results
Expected result: 2
Actual result without caching: 2
Actual result with caching: 1
The with-caching result is wrong, yes?
Upvotes: 2
Views: 3067
Reputation: 33101
There's no locking in the cache abstraction. You are not the first one asking for it and we already had significant brainstorming internally of what it would cost to support it. It turns out that it is far from being simple and playing with locks in such abstraction may be very tricky, especially in a "generic" way. Also, cache vendors spend a significant amount of resources to support these kind of use cases and there's a reason for that.
My best guess at it for the moment is that if you want these features, you need your cache to be transactional. There are some gotcha when using it with the abstraction though, check in particular SPR-11540 which is being worked on at the moment.
Upvotes: 1