Hot questions for Refreshing the Data in Guava

Top 10 Java Open Source / Guava / Refreshing the Data

How to automatically refresh Cache using Google Guava?

Question: 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?

Answer: 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.


Cached map with data initialization and whole data refreshed periodically in Java

Question: I need a cached map in my Java code. The map is loaded from DB and needs reloaded periodically from DB (for all of the data in the map). Since we can't import any new package now. Is there any existing function in google's guava package or code example?

It is better if the map is implemented as thread safe. But it is still OK if it is not but simple enough.

The "LoadingCache" is kind of what I like but it doesn't have data initialization method for me to put the data into the map at the beginning. And it needs to reach the DB for everytime "get" comes after the map expired.

Some sample codes may help here:

public interface AToBMapper
{

    public static final String DEFAULT_B_NAME = "DEFAULT";

    public String getBForA(final String a);
}

public class AToBMapperImpl implements AToBMapper
{
    private final SomeDAO dao;

    private Map<String, String> cachedMap;

    public AToBMapperImpl(final SomeDAO dao)
    {
        this.dao = dao;
        cachedMap = new HashMap<String, String>();
    }

    public String getBForA(final String a)
    {
        // if the map is not initialized, initialize it with the data
        // if the map is expired, refresh all the data in the map
        // return the mapped B for A (if there is no mapping for A, return the "DEFAULT")
    }

    private Map<String, String> getTheData(final List<String> listOfB)
    {
        Map<String, String> newData = dao.getAToBMapping(listOfB);
    }
}

Answer: Of course it does have such method - putAll(Map<K, V>) from Cache interface which LoadingCache extends. The method Copies all of the mappings from the specified map to the cache.

There's also similar put(K, V) method you can use for this purpose.

Here is simple example of what you could use (only JDK's and Guava's classes):

public class AToBMapperImpl implements AToBMapper {
  public static final long EXPIRE_TIME_IN_SECONDS =
      TimeUnit.SECONDS.convert(1, TimeUnit.HOURS); // or whatever
  private final SomeDAO dao;
  private final ConcurrentMap<String, String> cache;
  private final Stopwatch stopwatch;

  public AToBMapperImpl(SomeDAO dao) {
    this.dao = dao;
    stopwatch = new Stopwatch();
    cache = new MapMaker().concurrencyLevel(2).makeMap();
  }

  @Override
  public synchronized String getBForA(final String a) {
    // if the map is not initialized, initialize it with the data
    if (!stopwatch.isRunning()) {
      cache.putAll(getNewCacheContents());
      stopwatch.start();
    }

    // if the map is expired, refresh all the data in the map
    if (stopwatch.elapsedTime(TimeUnit.SECONDS) >= EXPIRE_TIME_IN_SECONDS) {
      cache.clear();
      cache.putAll(getNewCacheContents());
      stopwatch.reset();
    }

    // return the mapped String for A
    // (if there is no mapping for A, return the "DEFAULT")
    return cache.containsKey(a) ? cache.get(a) : new String(DEFAULT_B_NAME);
  }

  private Map<String, String> getNewCacheContents() {
    return getTheData(Arrays.asList("keys", "you", "want", "to", "load"));
  }

  private Map<String, String> getTheData(List<String> listOfB) {
    return dao.getAToBMapping(listOfB);
  }
}