How to empty Guava cache every 30 seconds while sending it to another method?

纵饮孤独 提交于 2019-12-31 03:26:13

问题


I am populating my guava cache from multiple threads by calling add method. Now from the background thread which runs every 30 seconds, I want to send whatever is there in the cache to sendToDB method atomically?

Below is my code:

public class Example {
  private final ScheduledExecutorService executorService = Executors
      .newSingleThreadScheduledExecutor();
  private final Cache<Integer, List<Process>> cache = CacheBuilder.newBuilder().maximumSize(100000)
      .removalListener(RemovalListeners.asynchronous(new CustomRemovalListener(), executorService))
      .build();

  private static class Holder {
    private static final Example INSTANCE = new Example();
  }

  public static Example getInstance() {
    return Holder.INSTANCE;
  }

  private Example() {
    executorService.scheduleAtFixedRate(new Runnable() {
      @Override
      public void run() {
        // is this the right way to send cache map?
        sendToDB(cache.asMap());
      }
    }, 0, 30, SECONDS);
  }

  // this method will be called from multiple threads
  public void add(final int id, final Process process) {
    // add id and process into cache
  }

  // this will only be called from single background thread
  private void sendToDB(ConcurrentMap<Integer, List<Process>> holder) {
    // use holder here

  }
}

Is this the right way to send cache map to my sendToDB method? Basically I want to send all the entries that are there in the cache for that 30 seconds and empty the cache out. After that my cache will get populated again in the next 30 seconds and then do the same process?

I think using cache.asMap() might not be the right way since it doesn't empty the cache out so it will reflect all the changes happening on the cache in my sendToDB method as well?


回答1:


How about:

@Override
public void run() {
  ImmutableMap<Integer, List<Process>> snapshot = ImmutableMap.copyOf(cache.asMap());
  cache.invalidateAll();
  sendToDB(snapshot);
}

This will copy the contents of the cache into a new map, creating a snapshot of the cache at a particular point in time. Then .invalidateAll() will empty the cache, after which the snapshot will be sent to the DB.

One disadvantage with this approach is it's racy - it's possible for entries to be added to the cache after the snapshot is created but before .invalidateAll() is called, and such entries would never be sent to the DB. Since your cache could also evict entries due to the maximumSize() setting I assume this isn't a concern, but if it is you'd want to instead remove the entry while constructing the snapshot, which would look like this:

@Override
public void run() {
  Iterator<Entry<Integer, List<Process>> iter = cache.asMap().entrySet().iterator();
  ImmutableMap<Integer, List<Process>> builder = ImmutableMap.builder();
  while (iter.hasNext()) {
    builder.add(iter.next());
    iter.remove();
  }
  sendToDB(builder.build());
}

With this approach the cache may not actually be empty when sendToDB() is invoked, but every entry that was there before the snapshotting began will have been removed and will be sent to the database.

Alternatively you could create a wrapper class that has a Cache field, and atomically swap the field out for a new empty cache, then copy the contents of the old cache to the database and allow it to be GCed.



来源:https://stackoverflow.com/questions/41777172/how-to-empty-guava-cache-every-30-seconds-while-sending-it-to-another-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!