44

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?

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
K. Siva Prasad Reddy
  • 11,786
  • 12
  • 68
  • 95

4 Answers4

34

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.

Frank Pavageau
  • 11,477
  • 1
  • 43
  • 53
  • 9
    FYI, bulk cache refresh operations are under discussion in Guava [issue 971](http://code.google.com/p/guava-libraries/issues/detail?id=971&q=bulk%20refresh&colspec=ID%20Stars%20Type%20Status%20Package%20Summary). – Louis Wasserman Jul 13 '12 at 16:16
  • I think you mean "CacheLoader.reload()", correct? Or has this method changed since this answer was written? – Nils Knappmeier Jun 13 '19 at 07:25
  • @NilsKnappmeier yes, good catch. `MyCacheLoader` is definitely not a `CacheBuilder`. – Frank Pavageau Jun 14 '19 at 13:12
13

JAVA 8 version with parallel stream:

Executors
        .newSingleThreadScheduledExecutor()
        .scheduleWithFixedDelay(() -> configurationCache
                .asMap()
                .keySet()
                .parallelStream()
                .forEach((key) -> configurationCache.refresh(key)),
            0,
            1, TimeUnit.SECONDS);
Cagatay Gurturk
  • 7,186
  • 3
  • 34
  • 44
9

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);
McKidoubled
  • 91
  • 1
  • 2
  • i looked in the reload method, when needsRefresh(key) is true , it will go to else statement , then it will go to execute the task , but i don't see it return the old value . i was thinking it should return the old value first and go for the new value asychronously . – Adams.H Sep 18 '14 at 05:52
  • 2
    as noted in https://code.google.com/p/guava-libraries/wiki/CachesExplained#Refresh the guava cache reload inherently does return the old value, while refreshing the cache asynchronously. `Refreshing is not quite the same as eviction. As specified in LoadingCache.refresh(K), refreshing a key loads a new value for the key, possibly asynchronously. The old value (if any) is still returned while the key is being refreshed, in contrast to eviction, which forces retrievals to wait until the value is loaded anew.` – McKidoubled Nov 21 '14 at 19:22
4

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()));
Divyansh
  • 41
  • 2
  • It doesn't provide a solution, because of It doesn't refresh a cache automatically. It is just a helper to refresh an entry, in the background, when the first stale request occurs to this entry. Without this wrapper an entry will be refreshed too, but synchronously. – v.ladynev May 03 '23 at 11:47