Skip to content

Confusion regarding caches' prop readWrite and mutability #3459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
bruderj15 opened this issue Apr 24, 2025 · 2 comments
Closed

Confusion regarding caches' prop readWrite and mutability #3459

bruderj15 opened this issue Apr 24, 2025 · 2 comments

Comments

@bruderj15
Copy link

bruderj15 commented Apr 24, 2025

Hello.

According to the docs here:
The cache will be treated as a read/write cache, meaning objects retrieved are not shared and can be safely modified by the caller, without interfering with other potential modifications by other callers or threads.

I think this is highly confusing.
Usually:

  • Read-Cache: Query once, read multiple times
  • Write-Cache: Write into buffer, flush later
  • R/W-Cache: Do both, often without invalidation on writes (reading buffered writes)

How does the latter entail that [...] objects retrieved are not shared and can be safely modified by the caller [...]?
This is just a wording thing.

My actual problem is modification - the data in the cache seems mutable (contrary to what is stated in the docs):

@CacheNamespace
class TaskMapper { @Select List<Task> findTasks() ... }

class TaskService {
  private TaskMapper taskMapper;  

  List<Task> getTasks() {
    List<Task> tasks = taskMapper.findTasks();
    foo(tasks); // effectful modification (side-effect)
    return tasks;
  }
}

Here, if foo(..) is not idempotent, TaskService::getTasks won't be either.

If we set the readWrite prop to false (previously defaulted to true):

@CacheNamespace(readWrite = true)
class TaskMapper { @Select List<Task> findTasks() ... }
// ...

Then TaskService::getTasks is idempotent, suggesting some weird connection between immutability and the (write) cache behavior.
There shouldn't at all - we didn't write anything here.

Is there an explanation?

@harawata
Copy link
Member

Hello @bruderj15 ,

I think you understand the doc correctly, but there also is a NOTE on that page:

Second level cache is transactional. That means that it is updated when a SqlSession finishes with commit or when it finishes with rollback but no inserts/deletes/updates with flushCache=true where executed.

Your code probably modifies the result before it gets cached.
If you call taskMapper.findTasks() beforehand (during startup, for example, just to cache unmodified results), modification by foo() won't affect the already-cached objects.

@bruderj15
Copy link
Author

You're right!
Indeed foo() was called within the same transaction - therefore modifying the data before actually storing it in the cache.

I was missing the NOTE, thank you @harawata!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants