7

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());
        }
    }

} 
Azzabi Haythem
  • 2,318
  • 7
  • 26
  • 32
Animesh
  • 71
  • 1
  • 1
  • 5
  • 1
    If you have the cache instance then you can call `invalidateAll()` to clear it. If you are using Spring Cache, you’ll need it check its documentation for a clear functionality. – Ben Manes Mar 01 '18 at 15:25
  • Hi Ben can you tell me, which cache instance should be used ? What should be the modification in above implementation ? – Animesh Mar 01 '18 at 15:28
  • I only see the builder, not the construction. I don’t use Spring and it seems like your questions are more about it than Caffeine. If so, perhaps ask in the Spring forum. – Ben Manes Mar 01 '18 at 15:33
  • On top of method i have just used @Cacheable("some_listname") annotation. Apart from that nothing has been used – Animesh Mar 01 '18 at 16:09
  • I think you want `@CacheEvict(allEntries=true)` according to the [docs](https://docs.spring.io/spring/docs/5.0.4.RELEASE/spring-framework-reference/integration.html#cache-annotations-evict) – Ben Manes Mar 01 '18 at 16:56
  • Also i want to check what values/elements are there in the cache or what all elements have been cached. How can i check the cached values – Animesh Mar 14 '18 at 12:24
  • You could inspect using the asMap() view – Ben Manes Mar 14 '18 at 14:19
  • @Ben: Can you please provide some code for it ? I tried but it was throwing me exception when i was trying the same – Animesh Mar 14 '18 at 15:23
  • System.out.println(cache.asMap()) should be enough to debug for starters – Ben Manes Mar 14 '18 at 15:28
  • I can use this cache.asMap () but lets say i have configured cache in some cacheConfig.java file and i want to get the values stored in the cache in some cacheUtility.java file in that case how will i get this cache there in the utility file. Also, considering i have n number of caches configured for my application based on specific names i.e. each cache have a separate name. In that scenario lets say i want cached data for cache named xyzCache then how will i get cached data for only that xyzCache ? – Animesh Mar 14 '18 at 16:49
  • Spring has a global `CacheManager` where each of the cache instances are stored, as a global registry. I think you could fetch the cache through that, right? – Ben Manes Mar 14 '18 at 17:53

1 Answers1

8

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 :)

Bikas Katwal
  • 1,895
  • 1
  • 21
  • 42
  • This is exactly what I want to do but I can't get the configuration to work because `org.springframework.cache.caffeine.CaffeineCacheManager` doesn't exist in my Spring version (4.3.10). Is that a Spring Boot-only package, or am I missing a dependency? I do have Caffeine in my deps and its packages are available. thx – Madbreaks Jan 04 '20 at 00:21