Caffeine cache refresh / reload cache manually or on demand

Animesh picture Animesh · Mar 1, 2018 · Viewed 10.5k times · Source

I have implemented caffeine cache in my application. I am caching data from few static tables. But i want to know if i can refresh / clear / reload cache manually or on demand using a REST API or any other way. Can any one please suggest a way to implement such a requirement.

I want something like :- an endpoint url like :- http://localhost:8080/refreshCache

this will trigger some method internally and clear the cache or reload new values in cache manually.

Below is the cache configuration:

@Configuration
public class CacheConfig{

     private com.github.benmanes.caffeine.cache.Cache<Object, Object> cache;

    @Bean
    Caffeine<Object,Object> cacheBuilder(){
        return Caffeine.newBuilder()
                .initialCapacity(300)
                .maximumSize(50000)
                .expireAfterAccess(1, TimeUnit.DAYS)
                .removalListener(new CacheRemovalListener())
                .recordStats();
    }

    class CacheRemovalListener implements RemovalListener<Object, Object> {
        @Override
        public void onRemoval(Object key, Object value, RemovalCause cause) {
            System.out.format("Removal listener called with key [%s], cause[%s], evicted [%s] %n", 
                    key , cause.toString(), cause.wasEvicted());
        }
    }

} 

Answer

Bikas Katwal picture Bikas Katwal · Sep 8, 2019

You can use Spring's CacheManager to create CaffeineCache instances and then you can perform CRUD operations on any cache using CacheManager.

See Below code.

Bean Configuration:

public class CacheBeansConfig {

  @Bean
  public CacheManager cacheManager() {
    // create multiple instances of cache
    CaffeineCacheManager cacheManager = new CaffeineCacheManager("UserCache","InventoryCache");
    cacheManager.setCaffeine(caffeineCacheBuilder());
    return cacheManager;
  }

  private Caffeine<Object, Object> caffeineCacheBuilder() {

    return Caffeine.newBuilder()
        .initialCapacity(<initial capacity>)
        .maximumSize(<max size>)
        .expireAfterAccess(<expire after hrs>, TimeUnit.HOURS)
        .recordStats();
  }

This will initialize your CacheManager with two Caffeeine Cache instances.

Use below Rest Controller Class to access these class.

@RestController
@RequestMapping(path = "/v1/admin/cache")
public class ACSCacheAdminController {

  @Autowired
  private CacheManager cacheManager;

  /**
   * call this to invalidate all cache instances
   */
  @DeleteMapping(
      path = "/",
      produces = {"application/json"})
  public void invalidateAll() {
    Collection<String> cacheNames = cacheManager.getCacheNames();
    cacheNames.forEach(this::getCacheAndClear);
  }

  /**
   * call this to invalidate a given cache name
   */
  @DeleteMapping(
      path = "/{cacheName}",
      produces = {"application/json"})
  public void invalidateCache(@PathVariable("cacheName") final String cacheName) {
    getCacheAndClear(cacheName);
  }

  /**
   * Use this to refresh a cache instance
   */
  @PostMapping(
      path = "/{cacheName}",
      produces = {"application/json"})
  public void invalidateCache(@PathVariable("cacheName") final String cacheName) {
    getCacheAndClear(cacheName);
    Cache cache = cacheManager.getCache(cacheName);
    // your logic to put in above cache instance
    // use cache.put(key,value)
  }


  /**
   * call this to invalidate cache entry by given cache name and cache key
   */
  @DeleteMapping(
      path = "/{cacheName}/{key}/",
      produces = {"application/json"})
  public void invalidateCacheKey(
      @PathVariable("cacheName") final String cacheName, @PathVariable("key") Object key) {
    final Cache cache = cacheManager.getCache(cacheName);
    if (cache == null) {
      throw new IllegalArgumentException("invalid cache name for key invalidation: " + cacheName);
    }
    cache.evict(key);
  }

  @GetMapping(
      path = "/{cacheName}/{key}",
      produces = {"application/json"})
  public ResponseEntity<Object> getByCacheNameAndKey(
      @PathVariable("cacheName") final String cacheName, @PathVariable("key") final int key) {
    final Cache cache = cacheManager.getCache(cacheName);
    if (cache == null) {
      throw new IllegalArgumentException("invalid cache name: " + cacheName);
    }
    return ResponseEntity.ok().body(cache.get(key));
  }

  private void getCacheAndClear(final String cacheName) {

    final Cache cache = cacheManager.getCache(cacheName);
    if (cache == null) {
      throw new IllegalArgumentException("invalid cache name: " + cacheName);
    }
    cache.clear();
  }

Just change the code as per your need :)