spring-projects / spring-projects/spring-framework

@CacheEvict(allEntries=true) may delete newly inserted keys when beforeInvocation=false

Open
#36,806 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

in: core status: waiting-for-triage
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
  • @CachePut return values are never null
  • No transactions involved (@Transactional removed)

Steps to Reproduce:

  1. Define an empty @CacheEvict method on a cacheName:
@CacheEvict(cacheNames = "xxx", allEntries = true)
public void clearCache() {}
  1. Call clearCache() and then insert multiple items using @CachePut:
clearCache();
putCache(item1);
putCache(item2);
  1. Use redis-cli MONITOR to 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 UNLINK command 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 beforeInvocation being 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.