Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guava CacheBuilder doesn't call removal listener

I want: Be notified when entity is removed due timeout expiration.

I tried: Set removal listener.

Problem: Seems removal listener doesn't work correct. It work only when I put new items into the cache (see code below)

Question: How to make removal listener work without putting new items?

Code:

My loading cache:

LoadingCache<String, Integer> ints = CacheBuilder.newBuilder()
            .maximumSize(10000)
            .expireAfterAccess(ACCESS_TIMEOUT, TimeUnit.MILLISECONDS)
            .removalListener(
                    new RemovalListener() {
                        //PROBLEM: THIS METHOD IS NEVER CALLED!!!
                        public void onRemoval(RemovalNotification notification) {
                            if (notification.getCause() == RemovalCause.EXPIRED) {
                                System.out.println("Value " + notification.getValue() + " has been expired");
                            } else {
                                System.out.println("Just removed for some reason");
                            }
                        }
                    }
            )
            .build(
                    new CacheLoader<String, Integer>() {
                        public Integer load(String key) throws Exception {
                            return new Integer(-1);
                        }
                    });

how I use cache in separate thread:

cache.put("key", 100);
Thread.sleep(ACCESS_TIMEOUT / 2);
System.out.println(cache.getIfPresent(key)); //returns 100
Thread.sleep(ACCESS_TIMEOUT * 5);
//CRUCIAL STRING: cache.put("key2", 200); //invoke removal listener
System.out.println(cache.getIfPresent(key)); //return null
//And again: the problem is that entity has been already expired, but removal listener isn't called untill I add new item to the cache.

P.S: I can share the complete demo at GitHub if you need, just tell me

like image 665
VB_ Avatar asked Feb 24 '14 11:02

VB_


1 Answers

It's because Guava does not ensure the eviction of the values automatically when the timeout value expires. It however does that during a series of read and write operations.

Per its documentation here:

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.

The reason for this is as follows: if we wanted to perform Cache maintenance continuously, we would need to create a thread, and its operations would be competing with user operations for shared locks. Additionally, some environments restrict the creation of threads, which would make CacheBuilder unusable in that environment.

To validate your onRemoval on expiration, call cache#cleanUp right before your 2nd read operation and it is supposed to call your onRemoval.

like image 138
StoopidDonut Avatar answered Nov 13 '22 04:11

StoopidDonut