question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Concurrency issue between get(K key, Callable<? extends V> valueLoader) and invalidate(Object key)

See original GitHub issue

Hi,

I encountered a concurrency issue between the “get(K key, Callable<? extends V> valueLoader)” and “invalidate(Object key)” methods with a basic (and i suppose a common) usage of the cache which let a stale value in the cache.

Here is the use case:

  • The cache is initially empty
  • 1 thread is getting a value in the callable with a given key while an other thread is invalidating the same given key

Thread 1

Bean myBean = cache.get(ID, new Callable<Bean>() {
    @Override
    public Bean call() throws Exception {
        Bean bean = loadFromDB(ID); // (1)
        return bean; // (4)
    }
});

Thread 2

// Update just one property of the bean in DB
updatePartialDataInDB(ID, "newValue1"); // (2)
// Then, we need to invalidate the cache
cache.invalidate(ID); // (3)

The execution sequence order is marked with // (number) After the point // 4, I have the old object in myBean variable which is fine. However, If i try to get again the bean with the identifier ID, I expect to have no value in the cache but a fresh bean reloaded from the DB. Unfortunately, that is not the case and a stale value is kept in the cache.

Is that a wanted behavior?

In LocalCache$Segment#remove(Object key, int hash) line 3081 to 3087 (guava 17.0):

RemovalCause cause;
if (entryValue != null) {
    cause = RemovalCause.EXPLICIT;
} else if (valueReference.isActive()) {
    cause = RemovalCause.COLLECTED;
} else {
    // currently loading
    return null;
}

Shouldn’t a particular process be done before return null ?

Thanks

Issue Analytics

  • State:open
  • Created 9 years ago
  • Reactions:3
  • Comments:17 (9 by maintainers)

github_iconTop GitHub Comments

5reactions
txshtkckrcommented, Apr 12, 2015

This is causing some very serious issues for us. I would argue that anyone using a lazy-loading cache would expect a happens-before relationship between invalidate and a following call to get, but since it can piggy-back on an existing load operation that fetched data before invalidate was called, that relationship does not exist.

I started poking around in here to try to fix it, but as @lowasser hinted, it isn’t trivial. The main problems are

1. Whether to remove the LoadingValueReference, block on it, or somehow mark it as stale; and
2. How to rework the removal notifications

This is sufficiently painful that at this point I’m inclined to switch to another library, instead.

2reactions
findepicommented, Jan 10, 2022

@ben-manes thanks for the update about Caffeine.

I am still looking for the Guava team’s perspective on the problem. In particular, would a patch be accepted? what solution categories have been discarded already? What are acceptable drawbacks, if any, for a potential fix?

@lowasser 's comment from 2014 (https://github.com/google/guava/issues/1881#issuecomment-67080198) suggests a patch would be welcome, but there was also a concern about value-in-flight invalidation (https://github.com/google/guava/issues/1881#issuecomment-67076595). Of course, both are couple years old, so they not necessarily describe current state.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Cache (Guava: Google Core Libraries for Java 15.0 API)
V · get(K key, Callable<? extends V> valueLoader). Returns the value associated with key in this cache, obtaining that value from valueLoader if...
Read more >
Cache (Guava: Google Core Libraries for Java 11.0 API)
V · get(K key, Callable<? extends V> valueLoader) Returns the value associated with key in this cache, obtaining that value from valueLoader if...
Read more >
Cache (The Adobe Experience Manager SDK 2021.11 ...
... public interface Cache<K,​V>. A semi-persistent mapping from keys to values. Cache entries are manually added using get(Object, Callable) or put(Object, ...
Read more >
LocalCache.java - Android Code Search
The upper bits of a key's hash code are used to choose ... public V get(K key, final Callable<? extends V> valueLoader) throws...
Read more >
Concurrent CacheLoader implementation - Google Groups
To report an issue: http://code.google.com/p/guava-libraries/issues/entry ... ListenableFuture<V> getFuture(K key, Callable<? extends V> valueLoader);.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found