38
votes

I am using Google Guava library for caching. For automatic cache refresh we can do as follows:

cache = CacheBuilder.newBuilder()               
                    .refreshAfterWrite(15, TimeUnit.MINUTES)
                    .maximumSize(100)
                    .build(....);

However, automatic refreshes are performed when the first stale request for an entry occurs.

Is there a way to refresh it automatically even though no requests came for cache data? Like for every 15 minutes the cache data should be pulled from Db and load it, no matter whether anybody called cache data or not.

Also, Guava's cache expiry time is for entire cache. Is it possible to expire cache values based on key? Like cache data with key "NOT_SO_FREQ_CHANGE_DATA" to expire for every 1 hour and data with key "FREQ_CHANGING_DATA" should expire for every 15 minutes?

5
Can u please give me the solution how you solved this?leostiw

5 Answers

32
votes

Guava provides no way to refresh the cache in bulk, but you can schedule a periodic refresh yourself:

LoadingCache<K, V> cache = CacheBuilder.newBuilder()
        .refreshAfterWrite(15, TimeUnit.MINUTES)
        .maximumSize(100)
        .build(new MyCacheLoader());

for (K key : cache.asMap().keySet()) {
    cache.refresh(key);
}

But in that case you may want to override the CacheLoader.reload(K, V) method in MyCacheLoader so it performs asynchronously.

As for the second question, no, you cannot set a per-entry expiration in Guava.

11
votes

JAVA 8 version with parallel stream:

Executors
        .newSingleThreadScheduledExecutor()
        .scheduleWithFixedDelay(() -> configurationCache
                .asMap()
                .keySet()
                .parallelStream()
                .forEach((key) -> configurationCache.refresh(key)),
            0,
            1, TimeUnit.SECONDS);
9
votes

1st question. Use a scheduled executor to kick off a periodic refresh.

2nd question. If you can infer your expiration policy from you cache key, or the previously cached value, it is possible to refresh you data at varying intervals.

based on this: https://code.google.com/p/guava-libraries/wiki/CachesExplained#Refresh

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
   .refreshAfterWrite(1, TimeUnit.MINUTES)
   .build(
       new CacheLoader<Key, Graph>() {
         public Graph load(Key key) { // no checked exception
           return getGraphFromDatabase(key);
         }

         public ListenableFuture<Graph> reload(final Key key, Graph prevGraph) {
           if (!needsRefresh(key,prevGraph)) {
             return Futures.immediateFuture(prevGraph);
           } else {
             // asynchronous!
             ListenableFutureTask<Graph> task = ListenableFutureTask.create(new Callable<Graph>() {
               public Graph call() {
                 return getGraphFromDatabase(key);
               }
             });
             executor.execute(task);
             return task;
           }
         }
       });

ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
ses.scheduleWithFixedDelay(
    new Runnable() {
        public void run() {
            for (Key key : graphs.asMap().keySet()) {
                graphs.refresh(key);
            }
        }
    }, 0, UPDATE_INTERVAL, TimeUnit.MINUTES);
2
votes

Can you reload the cache value automatically using the below code snippet, without calling the refresh method ??

cacher =
        CacheBuilder.newBuilder()
            .refreshAfterWrite(10, TimeUnit.SECONDS)
          .build(CacheLoader.asyncReloading(new CacheLoader<String, String>() {

              @Override
              public String load(String key) throws Exception {
                return method-call();
              }
            }, Executors.newSingleThreadExecutor()));
-3
votes

Here is some sample code to refresh a cache. Guava cache is easy to implement and also it is fast.

import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

public class GuavaTest {
    private static GuavaTest INSTANCE = new GuavaTest();

    public static GuavaTest getInstance(){
        return INSTANCE;
    }   

    private LoadingCache<String,String> cache;

    private GuavaTest() {
        cache = CacheBuilder.newBuilder()
                .refreshAfterWrite(2,TimeUnit.SECONDS)
                .build(new CacheLoader<String, String>() {
                        @Override
                        public String load(String arg0) throws Exception {
                            return addCache(arg0);
                        }
                });
    }

    private String addCache(String arg0) {
        System.out.println("Adding Cache");
        return arg0.toUpperCase();
    }

    public String getEntry(String args) throws ExecutionException{
        System.out.println(cache.size());
        return cache.get(args);
    }

    public static void main(String[] args) {
        GuavaTest gt = GuavaTest.getInstance();
        try {
            System.out.println(gt.getEntry("java"));
            System.out.println(gt.getEntry("java"));
            Thread.sleep(2100);
            System.out.println(gt.getEntry("java"));
            System.out.println(gt.getEntry("java"));
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

I referred this article guava cache example