Quantcast
Channel: Active questions tagged redis+java - Stack Overflow
Viewing all articles
Browse latest Browse all 2204

Race condition in springframework cache annotation?

$
0
0

I have been using @Cacheable and @CacheEvict in a multithreaded, multi node web app for several years now. As load on the system increases, I am started to notice null's being returned from the @Cacheable method even though null is not cached previously. It seems to be due to a race condition between cache evicts happening right before a cache get.

Sanitized sample code below:

@Cacheable(value = "CACHE_NAME", key = "#value", cacheManager = CACHE_MANAGER_NAME, unless = "#result == null")public Bar getBar(Foo foo) {  log.info("calculating Bar from Foo: {}", foo);  Bar bar = new Bar(foo.getValue() + 1);  log.info("calculated Bar: {}", bar);  return bar;}
@CacheEvict(value = "CACHE_NAME", key = "#value", cacheManager = CACHE_MANAGER_NAME)public void evict(Foo foo) {  log.info("evicted Foo: {}", foo);}
public Bar callingCode(Foo foo) {  Bar bar = getBar(foo);  log.info("got Bar: {} from Foo: {}", bar, foo);}

Here's what I saw recently in logs:

2022-05-27T05:10:39.976Z: "calculating Bar from Foo: Foo{value=1}"2022-05-27T05:10:39.983Z: "calculated Bar: Bar{value=2)"2022-05-27T05:10:39.985Z: "got Bar: Bar{value=2) from Foo: Foo{value=1}"2022-05-27T05:10:40.203Z: "evicted Foo: Foo{value=1}"2022-05-27T05:10:40.205Z: "got Bar: null from Foo: Foo{value=1}"2022-05-27T05:10:40.209Z: "calculating Bar from Foo: Foo{value=1}"2022-05-27T05:10:40.215Z: "calculated Bar: Bar{value=2)"2022-05-27T05:10:40.226Z: "got Bar: Bar{value=2) from Foo: Foo{value=1}"

My conclusion is that there is some non-thread-safe code in the @Cacheable implementation, such that if it is called at the same time as a @CacheEvict, it may register as a cache hit but then return null.

I have thought of a few workarounds:

  1. Add concurrency code around getBar() and evict() methods so that they cannot be called concurrently, and must wait for the other to finish
  2. Add retry code so that if getBar() returns null, we evict the cache again and then retry getBar() again.

But I wanted to see if there was some feature / setting in this spring framework code that I am just not aware of, that I should be using to prevent this race condition from occurring. I was also surprised to see that I couldn't find this question asked before.


Viewing all articles
Browse latest Browse all 2204

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>