guava: LoadingCache.getIfPresent(key) returns null after LoadingCache.getUnchecked(key) returned non null value
I am using the LoadingCache in a highly concurrent system and I observed some behavior that I am not sure is intended (to me it looks like a bug š ). I wrote the following sample code to illustrate the problem :
public static void main(String[] args) {
final LoadingCache<String, Object> cache =
CacheBuilder.newBuilder()
.expireAfterAccess(65, TimeUnit.MINUTES)
.build(new CacheLoader<String, Object>(){
@Override
public Object load(String arg0) throws Exception {
Object obj = new Object();
System.out.println("creating object: " + obj);
return obj;
}
});
int threadCount = 600;
final String key = "hello/world";
Runnable task = new Runnable() {
@Override
public void run() {
try {
Object valueFromUnchecked = cache.getUnchecked(key);
if (valueFromUnchecked == null) {
System.out.println(Thread.currentThread().getName() + " valueFromUnchecked is null!!!");
}
Object value = cache.getIfPresent(key);
if (value == null) {
System.out.println(Thread.currentThread().getName() + " value is null!!!");
}
if (value != valueFromUnchecked) {
System.out.println(String.format(Thread.currentThread().getName() + "valueFromUnchecked:%s, value:%s", valueFromUnchecked, value));
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
for (int i = 0; i < threadCount; i++) {
Thread t = new Thread(task);
t.setName("thread-" + i);
t.start();
}
Sometimes thing are fine, I donāt get the output of ā<thread name> value is null!!!ā lines and some times I do. I never get the output of āvalueFromUnchecked from getUnchecked is null!!!ā
My understanding of from LoadingCache java doc, is that if getUnchecked()
does not return null then getIfPresent()
shouldnāt return null as well; given that the cache has not expired yet.
About this issue
- Original URL
- State: open
- Created 9 years ago
- Comments: 15 (11 by maintainers)
Commits related to this issue
- Attempt to address https://github.com/google/guava/issues/2131 by removing the if (count != 0) check. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=105235264 — committed to google/guava by lowasser 9 years ago
- Automated g4 rollback of changelist 105235264. *** Reason for rollback *** It turns out this doesn't fix the Guava issue. As such, I think it's probably preferable to keep the count != 0 check as it... — committed to google/guava by cgdecker 9 years ago
- Workaround for https://github.com/google/guava/issues/2131 LoadingCache.getIfPresent returns null sometimes even when the entry is being loaded by another thread. This leads to wrong invalidations and... — committed to qubole/rubix by shubhamtagra 8 years ago
Unfortunately I donāt have the time to assist in maintaining Guavaās Cache, and like the other coauthors, have moved on. I would prefer to have the difference between libraries be primarily performance and features for new code, rather than have users deal with bugs in their existing code. I can help review patches if anyone is interested in sending bug fixes.
I am familiar with the code base but Charles and I inherited it from Bob, where it was already very complex by the time we started adding / rewriting the caching logic. Unfortunately we didnāt realize the linearization problems early enough so there were some original sins, likely further mistakes introduced later on, and we were both 20%ers (with Charles going full time for a short period). For example how to handle explicit writes for an in-flight load is not linearized (puts stomp the load whereas removals no-op, iirc). When writing narrow portions from the perspective of a transient best-effort cache the issues seemed benign, but taking a step back itās clearly wrong. The overwhelming amount of code due to forking ConcurrentHashMap makes it hard to wade through. Caffeine benefits from decorating the map, the addition of Java 8ās computes, being much more test driven, and learning from these past experiences.
I wrote a simple Lincheck test below that could be extended and drive iterations on fixes for these linearization problems. However as @cgdecker mentioned since this cache is in maintenance mode, if possible then please try Caffeine where Iāve tried to be more diligent on correctness.
Lincheck Test
I think itās unlikely weāll fix this at this point, though if someone wants to contribute a fix weād be happy to look at that.
Note that we recommend people use Caffeine instead of Guavaās cache; itās based on Guavaās cache APIs but maintained by @ben-manes who has far more caching expertise than anyone on the Guava team these days.
I am also facing similar issue, can any one tell me which version I should use where the fix has been applied? However I am not calling
getUnchecked
I am directly callinggetIfPresent
function and seeing null value sometimes.