Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to temporarily disable caching for Spring cache

I have a spring bean annotated with @Cacheable annotations defined like so

@Service
public class MyCacheableBeanImpl implements MyCacheableBean {
    @Override
    @Cacheable(value = "cachedData")
    public List<Data> getData() { ... }
}

I need this class to be capable of disabling caching and working only with data from original source. This should happen based on some event from the outside. Here's my approach to this:

@Service
public class MyCacheableBeanImpl implements MyCacheableBean, ApplicationListener<CacheSwitchEvent> {
    //Field with public getter to use it in Cacheable condition expression
    private boolean cacheEnabled = true;

    @Override
    @Cacheable(value = "cachedData", condition = "#root.target.cacheEnabled") //exression to check whether we want to use cache or not
    public List<Data> getData() { ... }

    @Override
    public void onApplicationEvent(CacheSwitchEvent event) {
        // Updating field from application event. Very schematically just to give you the idea
        this.cacheEnabled = event.isCacheEnabled();
    }

    public boolean isCacheEnabled() {
        return cacheEnabled;
    }

}

My concern is that the level of "magic" in this approach is very high. I'm not even sure how I can test that this would work (based on spring documentation this should work but how to be sure). Am I doing it right? If I'm wrong then how to make it right?

like image 948
SimY4 Avatar asked Mar 10 '16 09:03

SimY4


People also ask

How do I evict a cache in spring boot?

Spring provides two ways to evict a cache, either by using the @CacheEvict annotation on a method or by auto-wiring the CacheManger and clearing it by calling the clear() method.

How do I turn on spring cache?

We can enable caching in the Spring Boot application by using the annotation @EnableCaching. It is defined in org. springframework. cache.

Does spring boot cache by default?

3.1. In spring boot project, we need to add it to the boot application class annotated with @SpringBootApplication . Spring provides one concurrent hashmap as default cache, but we can override CacheManager to register external cache providers as well easily.

What is the use of @cacheable?

As the name implies, @Cacheable is used to demarcate methods that are cacheable - that is, methods for whom the result is stored into the cache so on subsequent invocations (with the same arguments), the value in the cache is returned without having to actually execute the method.


2 Answers

Inspired by SimY4 last comment, here is my working solution overloading SimpleCacheManager in order to provide runtime switch. Just use switchableSimpleCacheManager.setEnabeld(false/true) to switch off/on.

package ch.hcuge.dpi.lab.cache;

import org.springframework.cache.Cache;
import org.springframework.cache.support.NoOpCache;
import org.springframework.cache.support.SimpleCacheManager;

/**
* Extends {@link SimpleCacheManager} to allow to disable caching at runtime
*/
public class SwitchableSimpleCacheManager extends SimpleCacheManager {

  private boolean enabled = true;

  public boolean isEnabled() {
      return enabled;
  }

/**
 * If the enabled value changes, all caches are cleared
 *
 * @param enabled true or false
 */
public void setEnabled(boolean enabled) {
    if (enabled != this.enabled) {
        clearCaches();
    }
    this.enabled = enabled;
}

@Override
public Cache getCache(String name) {
    if (enabled) {
        return super.getCache(name);
    } else {
        return new NoOpCache(name);
    }
}

protected void clearCaches() {
    this.loadCaches().forEach(cache -> cache.clear());
}

}

Configuration ( using Caffeine ):

@Bean
public SwitchableSimpleCacheManager cacheManager() {
    SwitchableSimpleCacheManager cacheManager = new SwitchableSimpleCacheManager();
    cacheManager.setCaches(Arrays.asList(
            buildCache(RESULT_CACHE, 24, 5000)
    ));
    return cacheManager;
}

private CaffeineCache buildCache(String name, int hoursToExpire, long maxSize) {
    return new CaffeineCache(
            name,
            Caffeine.newBuilder()
                    .expireAfterWrite(hoursToExpire, TimeUnit.HOURS)
                    .maximumSize(maxSize)
                    .build()
    );
}
like image 161
A. Parolini Avatar answered Oct 22 '22 14:10

A. Parolini


What I was looking for was NoOpCacheManager:

To make it work I switched from xml bean creation to a factory

I did something as follows:

    @Bean
public CacheManager cacheManager() {
    final CacheManager cacheManager;        
    if (this.methodCacheManager != null) {
        final EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager();
        ehCacheCacheManager.setCacheManager(this.methodCacheManager);
        cacheManager = ehCacheCacheManager;
    } else {
        cacheManager = new NoOpCacheManager();
    }

    return cacheManager;
}
like image 37
adramazany Avatar answered Oct 22 '22 15:10

adramazany