I am having a Loading Cache like this :
MyCacheLoader loader=new MyCacheLoader();
MyRemovalListener listener=new MyRemovalListener();
LoadingCache<String, String> myCache = CacheBuilder.newBuilder()
.concurrencyLevel(10)
.weakKeys()
.maximumSize(2)
.expireAfterWrite(120, TimeUnit.SECONDS)
.removalListener(listener)
.build(loader);
But when I do something like this to test :
System.out.println(myCache.get("100"));
System.out.println(myCache.get("103"));
System.out.println(myCache.get("110"));
System.out.println(myCache).get("111"));
Thread.sleep(230000);
System.out.println(myCache.size());
Am still getting 2 and not zero. Why ? After more than 120 seconds I should have got zero size if am not wrong ?
MyCacheLoader
public class MyCacheLoader extends CacheLoader<String,String> {
@Override
public String load(String key) throws Exception {
return key;
}
}
MyRemovalListener
public class MyRemovalListener implements RemovalListener<String,String> {
Logger logger = LoggerFactory.getLogger(MyRemovalListener.class);
@Override
public void onRemoval(RemovalNotification<String, String> removalNotification) {
logger.info("Message with message Id ("+
removalNotification.getKey()+ ") is removed.");
System.out.println("Message with message Id ("+
removalNotification.getKey()+ ") is removed.");
}
}
First, if you are new to Guava's Cache API or if you are treating it as if it implements the Map<K, V>
interface then I recommend reading CachesExplained · google/guava Wiki. "A Cache
is similar to ConcurrentMap
, but not quite the same."
e.g. Cache.size()
"returns the approximate number of entries in this cache" (emphasis added) unlike Map.size()
which "returns the number of key-value mappings in this map."
As such, we should not expect Cache.size()
to return an exact number of entries but an approximate one. I would not generally make assertions on Cache.size()
although I certainly would on Map.size()
.
Second, "caches built with CacheBuilder
do not perform cleanup and evict values 'automatically,' or instantly after a value expires, or anything of the sort. Instead, it performs small amounts of maintenance during write operations, or during occasional read operations if writes are rare" (When Does Cleanup Happen? · CachesExplained · google/guava Wiki).
Consider the following example:
LoadingCache<String, String> myCache = CacheBuilder.newBuilder()
.concurrencyLevel(10)
.weakKeys()
.maximumSize(2)
.expireAfterWrite(120, TimeUnit.MILLISECONDS)
.removalListener(notification ->
System.out.println(notification.getKey() + " removed"))
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
System.out.println("loading " + key);
return UUID.randomUUID().toString();
}
});
myCache.get("100");
myCache.get("103");
myCache.get("110");
myCache.get("111");
Thread.sleep(230);
System.out.println(myCache.size());
myCache.get("111");
System.out.println(myCache.size());
Ouptut:
loading 100
loading 103
loading 110
100 removed
loading 111
103 removed
2
110 removed
111 removed
loading 111
1
As you can see, evictions here do not occur until items are read from the cache.
For more details along with a more detailed example see my answer to How does timed cache expiry work?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With