Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guava CacheBuilder Cache RemovalListener onRemoval doesn't get called everytime though the entry gets removed

Tags:

java

guava

I have created a Guava CacheBuilder based cache with on expiry of 5 seconds if key is not written to. Have added a removalListener to it which prints the key/value pair being removed. What I have observed is that the onRemoval method of the listener gets called only the first time. It doesn't get called the second time an entry is removed. (The actual removal happens. Just the onRemoval method of the removalListener doesn't get called).

Am I doing something wrong? Can somebody help? Thanks in advance. Here's my code:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;


public class TestCacheBuilder {

  public static void main(String[] args) {
    try {
      new TestCacheBuilder();
    }catch (Exception e){      
     e.printStackTrace(); 
    }
  }

  public TestCacheBuilder() {

    Cache<String, String> myCache = CacheBuilder.newBuilder()
        .expireAfterWrite(5, TimeUnit.SECONDS)
        .removalListener(new RemovalListener<String, String>() {
          public void onRemoval(RemovalNotification<String, String> removal) {
            System.out.println("removal: "+removal.getKey()+"/"+removal.getValue());
          }          
        })
        .build();


    Map<String, String> inMap = myCache.asMap();

    inMap.put("MyKey", "FirstValue");

    System.out.println("Initial Insert: "+inMap);

    //Wait 16 seconds

    try {
      Thread.sleep(4000);
    } catch(InterruptedException ex) {
        Thread.currentThread().interrupt();
    }

    System.out.println("After 4 seconds: " + inMap);

    inMap.put("MyKey", "SecondValue");

    try {
      Thread.sleep(1000);
    } catch(InterruptedException ex) {
        Thread.currentThread().interrupt();
    }

    System.out.println("After 1 more second: " + inMap);

    try {
      Thread.sleep(4000);
    } catch(InterruptedException ex) {
        Thread.currentThread().interrupt();
    }


    System.out.println("After 4 more seconds: " + inMap);

  }

}

The output is as below:

Initial Insert: {MyKey=FirstValue}
After 4 seconds: {MyKey=FirstValue}
removal: MyKey/FirstValue
After 1 more second: {MyKey=SecondValue}
After 4 more seconds: {}
like image 687
Kiran Avatar asked Aug 28 '12 10:08

Kiran


People also ask

Is Guava cache persistent?

README.md. A Guava cache extension that allows caches to persist cache entries when they cannot longer be stored in memory. An implementation that overflows to the file system is provided including a corresponding CacheBuilder with similar semantics than the Guava CacheBuilder .

How does Guava cache work?

Guava provides a very powerful memory based caching mechanism by an interface LoadingCache<K,V>. Values are automatically loaded in the cache and it provides many utility methods useful for caching needs.

Where is Guava cache stored?

Guava Caches store values in RAM.

What is Guava loading cache?

A semi-persistent mapping from keys to values. Values are automatically loaded by the cache, and are stored in the cache until either evicted or manually invalidated. Implementations of this interface are expected to be thread-safe, and can be safely accessed by multiple concurrent threads.


2 Answers

The removal doesn't actually happen directly: Guava does not have its own cleaning thread to remove expired entries. The removals happen usually when there's a write in the same segment of the cache, or during a read when a certain amount of time has passed (to amortize the cost of the removal). However, while the entry is still there, it's seen as expired, which is why it's not printed.

Quoting CacheBuilder's javadoc:

If expireAfterWrite or expireAfterAccess is requested entries may be evicted on each cache modification, on occasional cache accesses, or on calls to Cache.cleanUp(). Expired entries may be counted in Cache.size(), but will never be visible to read or write operations.

like image 190
Frank Pavageau Avatar answered Sep 25 '22 02:09

Frank Pavageau


As an alternative to Guava Cache you can use Caffeine, "a high performance, near optimal caching library based on Java 8".

It has "Google Guava inspired API" and performs eviction periodically, so it may fit your use case better (if you're worried about memory not being freed semi-automatically). See Eviction wiki page:

Expiration is performed with periodic maintenance during writes and occasionally during reads. Scheduling and firing of an expiration event is executed in amortized O(1) time.

like image 34
Xaerxess Avatar answered Sep 24 '22 02:09

Xaerxess