Guava Cache: github.com/google/guav…

Guava Cache has been used as a Cache pool for some time. For example, when is the expiration date? So I decided to learn more about it.

Example:

/** * Pooled RestHighLevelClient, Avoid creating connections every time */ LoadingCache<EngineDatasourceQuery, RestHighLevelClientHolder> externalEsClientCache = CacheBuilder .newBuilder() .expireAfterAccess(4, TimeUnit.HOURS) .removalListener((RemovalListener<EngineDatasourceQuery, RestHighLevelClientHolder>) n -> { log.info("close es client, engineId: {}", n.getValue().getEngineId()); RestHighLevelClientFactory.close(n.getValue().getClient()); }) .build(new CacheLoader<EngineDatasourceQuery, RestHighLevelClientHolder>() { @Override public RestHighLevelClientHolder load(EngineDatasourceQuery engineDatasourceQuery) { int engineId = engineDatasourceQuery.getEngineId(); KpiDatasource datasource = engineDatasourceQuery.getDatasource(); log.info("load datasource, engineId: {}, code: {}", engineId, datasource.getUnicode()); RestHighLevelClient restHighLevelClient = RestHighLevelClientFactory.create( new String[]{datasource.getUrl()}, StringUtils.defaultString(datasource.getUserName()), StringUtils.defaultString(datasource.getPassword())); return new RestHighLevelClientHolder(engineId, datasource.getUnicode(), restHighLevelClient); }});Copy the code
  1. Refresh (K), loadingCache.refresh (K), refresh the value of the specified key. The refresh process is asynchronous internally (the current thread still blocks until the refresh is complete), and the old value is retrieved until the refresh is complete. in contrast to eviction, which forces retrievals to wait until the value is loaded anew. Reload can be done by overwriting the cacheloader.reload (K, V) method, which computs new values from old ones on reload. The official example is posted below:

    // Some keys don't need refreshing, and we want refreshes to be done asynchronously. LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder() .maximumSize(1000) .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 (neverNeedsRefresh(key)) { 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; }}});Copy the code
  2. Manually delete data

    • Delete a single key using cache.invalidate (key).
    • Delete keys in batches using cache. invalidateAll(keys).
    • Delete all keys with cache.invalidateAll ()
  3. Listen to delete data, implement interface RemovalListener, the default listener operation synchronization, if the operation is slow, can make the operation asynchronous, Using RemovalListeners. Asynchronous (RemovalListener, Executor) to decorate RemovalListener

  4. The deletion of expired elements is not performed in real time, which means that elements are not removed from the cache as soon as they expire. Here’s why:

    Caches built with CacheBuilder do not perform cleanup and evict values “automatically,” or instantly after a value expires, or anything of the sort. Instead, it performs small amounts of maintenance during write operations, or during occasional read operations if writes are rare.

    The reason for this is as follows: if we wanted to perform Cache maintenance continuously, we would need to create a thread, and its operations would be competing with user operations for shared locks. Additionally, some environments restrict the creation of threads, which would make CacheBuilder unusable in that environment.

    Instead, we put the choice in your hands. If your cache is high-throughput, then you don’t have to worry about performing cache maintenance to clean up expired entries and the like. If your cache does writes only rarely and you don’t want cleanup to block cache reads, you may wish to create your own maintenance thread that calls Cache.cleanUp() at regular intervals.

    The listener of RemovalListener will not be called immediately if the cache cache is deleted. The listener may not be called until the cache cache is deleted. So be careful if you want to use this method to do some real-time processing of deleted data. Guava Cache also provides a way to supplement this by manually executing the cache.cleanup () method, which cleans up all expired elements.

    The listener methods I’ve tried so far that immediately trigger the removal of an element and execute RemovalListener are:

    • Manually Deleting Data
    • The refresh data
    • The cache is set to maximumSize and the number of elements exceeds maximumSize
    • The cache.cleanup () method is called with an expired element
    • Call cache.put() or cache.asmap ().put() to overwrite the value of the existing key
    • Call the cache.asmap ().remove method

The ~