16

I am using Spring Boot and for caching I am using Ehcache. It's working fine till now. But now I have to reload / refresh so how can I do this so that my application will not be having any downtime.

I have tried many ways in Spring Ehcache but it didn't work or Else have to write a scheduler and reload the data.

@Override
@Cacheable(value="partTypeCache", key="#partKey")
public List<PartType> loadPartType(String partKey) throws CustomException {
        return productIdentityDao.loadPartType();
}
Eugene Kortov
  • 445
  • 6
  • 17
Shubuu
  • 161
  • 1
  • 1
  • 5
  • is CacheEvict what you are looking for? : https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#cache-annotations-evict – ItFreak Jun 05 '19 at 10:50
  • 1
    @ItFreak CacheEvict is used to remove/clear cache, but in my case i have to reload/refresh cache so that it will be in SYNC with ny database. – Shubuu Jun 06 '19 at 04:11
  • I do not think that there is a out-of-the-box solution as that would intend that the framework knows all the underlying data sources. – ItFreak Jun 06 '19 at 06:08
  • 2
    CacheEvict will remove value from cache. I want to reload the hole cache after a fix interval of time without having issue – Shubuu Jun 10 '19 at 14:48

6 Answers6

18

Apparently all the comments about your problem were right. You should use CacheEvict. I found solution here: https://www.baeldung.com/spring-boot-evict-cache and it looks like this:

All you have to do is create class called e.g. CacheService and create method that will evict all cached objects you have. Then you annotate that method @Scheduled and put your interval rate.

@Service
public class CacheService {

    @Autowired
    CacheManager cacheManager;

    public void evictAllCaches() {
        cacheManager.getCacheNames().stream()
          .forEach(cacheName -> cacheManager.getCache(cacheName).clear());
    }

    @Scheduled(fixedRate = 6000)
    public void evictAllcachesAtIntervals() {
        evictAllCaches();
    }

}
Dharman
  • 30,962
  • 25
  • 85
  • 135
J.Kennsy
  • 596
  • 1
  • 6
  • 19
  • I want to unit test cacheservice.evictAllcaches. I observed cacheManager.getCacheNames() returns same cache values after clearing cache. Any idea how to test empty cache scenario. – StackOverFlow Jul 01 '20 at 12:55
  • Since `Collection` also has a `forEach` method, the solution can be simplified a little by removing the `.stream()` call: `cacheManager.getCacheNames().forEach` – M. Justin Apr 26 '23 at 18:30
4

The option suggested by many to use @CacheEvict is correct. Additionally, to ensure your cache (near) always has latest data loaded, even if data is updated in database out of bounds to your running app, you need to reload the whole dataset periodically with an interval matching your app's SLAs. In the solution suggested above, add logic to reload all data like below:

@Service
public class CacheService {

    @Autowired
    CacheManager cacheManager;

    public void refreshAllCaches() {
        cacheManager.getCacheNames().stream()
          .forEach(cacheName -> cacheManager.getCache(cacheName).clear());
        // reload whole dataset here, dummy example here:
        dataRepository.findAll().forEach(a -> cacheManager.getCache("cache-name")).put(a.getKey(), a));
    }

    @Scheduled(fixedRate = 6000)
    public void refreshAllcachesAtIntervals() {
        refreshAllCaches();
    }

}
aboger
  • 2,214
  • 6
  • 33
  • 47
V_Singh
  • 729
  • 11
  • 22
2

Try something like this, as mentioned in comments also:

    @Caching(evict={@CacheEvict(value="partTypeCache", key="#partKey")})
    public boolean deletePartType(String partKey) { 
      //when this method is invoked the cache is evicted for the requested key
    }
TechFree
  • 2,600
  • 1
  • 17
  • 18
  • 1
    thanks for response.CacheEvict will clear the cache, but i have to refresh cache so that it will be in SYNC with DataBase – Shubuu Jun 06 '19 at 04:13
  • 2
    When @Cacheable executes, then it would not find data in cache, and would automaticlly refresh/repopulate the cache. – TechFree Jun 06 '19 at 06:13
  • But what i know is that @Cacheable is used to load data in cache and this is working fine but suppose new value is added from different source like **`ETL`** ,**`Different Application`**. Then in that case i need to reload the cache. – Shubuu Jun 06 '19 at 07:56
  • @ Cacheable invocation will update cache also. However, you can also use @ CachePut to updaet the cache Refer : https://www.concretepage.com/spring/spring-cacheput-annotation-example-using-javaconfig. Now method with annotation @ CachePut needs to be invoked when there is an update to cache fromm any source. I am assuming you have the message/link/trigger/handle to an update of cache from your different application – TechFree Jun 06 '19 at 08:19
  • No those trigger are not handled because there are various SYNC process whose directly dump data into DataBase. Is there any way in spring where i can directly reload the hole data/newly added data into cache.One way is that write a scheduler and execute it on a regular interval. Do you have some better suggestion? – Shubuu Jun 06 '19 at 08:44
1

Apart from above answers you can use cron inside your scheduler which gives more flexibility. you can have a scheduler running daily,weekly ,yearly,daily at 12pm etc whiout writing much code.

@Service public class CacheService {

@Autowired
CacheManager cacheManager;

public void evictAllCaches() {
    cacheManager.getCacheNames().stream()
      .forEach(cacheName -> cacheManager.getCache(cacheName).clear());
}

@Scheduled(cron = "@weekly")
public void evictAllcachesAtIntervals() {
    evictAllCaches();
}

}

spidy
  • 539
  • 1
  • 6
  • 7
0

Do you know what partKeys you are looking to renew specifically? If you don't, you'll have to find the keys from the cache itself. You can use @CachePut to update cache values with no downtime. The performance of my suggestion will not be great, but it should do exactly what you're looking for.

For EhCache specifically:

public void renewCache() {
    net.sf.ehcache.EhCache cache = (net.sf.ehcache.EhCache) org.springframework.cache.CacheManager.getCache("partTypeCache").getNativeCache();
    for (Object key : cache.getKeys()) {
          updatePartTypeCache((String) key)
    }
}

@CachePut(value="partTypeCache", key="#partKey")
public List<PartType> updatePartTypeCache(String partKey) throws CustomException {
        return productIdentityDao.loadPartType();
}

To be clear, the worst part of this is getting keys from the existing cache. If you have a short list of "hot" parts that you're wanting to keep in cache, you could just call updatePartTypeCache for those instead.

Alex Petty
  • 28
  • 3
0

The requirement is to refresh 1 cache at X interval and another cache at Y interval. If we write the below code in only 1 class then the cache NOT gets reloaded.


public class XXXSchedulers {

    @Autowired
    private XXXUtil util;

    @Scheduled(fixedDelay = 10 * 60 * 1000) // Running after 10 minutes
    public void clearSpecificABCCache() {
        util.clearSpecificABCCache();
        util.getABC();//Again gets value in the cache
    }

    @Scheduled(cron = "0 0 2 * * ?") //Running Everyday at 2 AM public void 
    public void clearSpecificXYZCache() {
        util.clearSpecificABCCache();
        util.getXYZ();
    }
}

@Component
public class XXXUtil {
    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private XXXService service;

    @Cacheable("abc")
    public ABC getABC() {
        ABC abc = service.getABC();
    }

    public void clearSpecificABCCache() {
        cacheManager.getCache("abc").clear();
    }

    @Cacheable("xyz")
    public XYZ getXYZCache() {
        XYZ xyz = service.getXYZCache();
    }
    
    public void clearSpecificXYZCache() {
        cacheManager.getCache("xyz").clear();
    }
  }
AmolRavan
  • 33
  • 6