spring-projects / spring-projects/spring-framework
@CacheEvict(allEntries=true) may delete newly inserted keys when beforeInvocation=false
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 60.2k
- Forks
- 38.8k
- Avg merge
- 5d 2h
- Merged PRs (30d)
- 27
Description
When using Spring Cache with Redis, we observed a potential issue with @CacheEvict(allEntries=true) in combination with @CachePut that leads to intermittent deletion of newly inserted keys.
Environment:
- Spring Boot: 4.0.6
- Redis: 8.6.2
- Redis: single node, TTL=-1 for all keys, non-clustered, single instance
- CacheWriter: default non-locking RedisCacheWriter
- Method visibility: public
@CachePutreturn values are never null- No transactions involved (
@Transactionalremoved)
Steps to Reproduce:
- Define an empty
@CacheEvictmethod on a cacheName:
@CacheEvict(cacheNames = "xxx", allEntries = true)
public void clearCache() {}
- Call
clearCache()and then insert multiple items using@CachePut:
clearCache();
putCache(item1);
putCache(item2);
- Use
redis-cli MONITORto track commands.
Observed Behavior:
- With
beforeInvocation=false(default), the command sequence may be:
keys cacheName::*
set cacheName::item1 ...
unlink keys_from_snapshot
set cacheName::item2 ...
-
The
UNLINKcommand may asynchronously delete the newly inserted first item, causing it to disappear. -
This behavior occurs intermittently.
-
When
beforeInvocation=true, the command sequence becomes:
keys cacheName::*
del keys_from_snapshot
set cacheName::item1 ...
set cacheName::item2 ...
- In this case, all newly inserted items are preserved.
Questions:
- Why are the values of
beforeInvocationbeing changed using two different instructions, namely DEL and UNLINK?? - Mechanically speaking, how exactly does UNLINK get inserted into the middle of the SET command for execution?
- Is this behavior documented or expected, and should beforeInvocation=true be recommended whenever using allEntries=true with Redis and non-locking CacheWriter?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Spring Cache's default non-locking RedisCacheWriter and reproduce the sequence using redis-cli MONITOR. Trace how @CacheEvict(allEntries=true), beforeInvocation=false, and @CachePut interact, then compare DEL and UNLINK behavior; done means explaining whether the deletion race is expected and identifying any documentation or implementation change needed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, redis
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100