0

I have it currently set up to expire after 12 hours. However, it expires 12 hours after each cache is first written too. I would like it to refresh at 12am and 12pm only. Is this possible? In my cacheConfig file I have:

@Component
@EnableCaching
public class CacheConfig {

   @Bean
   public Caffeine defaultCacheConfig() {
       return Caffeine.newBuilder()
               .expireAfterWrite(12, TimeUnit.HOURS);
   }
}

I am using Caffeine Cache library.

Brian Akumah
  • 144
  • 3
  • 12

2 Answers2

2

I believe that Caffeine does not support this kind of scheduling. But if that's strong requirement and should be implemented as follows - you may use the Spring's @Scheduled annotation which allows to use Cron config. You would be able to read about it here: https://www.baeldung.com/spring-scheduled-tasks

So for my vision it could work in the following way:

  • Setup a Scheduled Spring service and configure required Cron. Autowire CacheManager via field or constructor and setup the refreshCache() to clear all the caches for the Caffeine manager. I'll leave an example of code but not sure if it 100% works :)

      @Component
      public class CacheRefreshService {
    
         @Autowired
         private CacheManager cacheManager;
    
         @Scheduled(cron = ...)
         public void refreshCache() {
            cacheManager.getCacheNames().stream()
               .map(CacheManager::getCache)
               .filter(Objects::nonNull)
               .forEach(cache -> cache.clear());
         }
     }
    

And don't forget to put @EnableScheduling for your @Configuration-s or you could add it aside to @SpringBootApplication if you are running one.

2

Caffeine supports variable expiration, where the duration for an entry has to be calculated independently. If you wanted to all entries expire at the same time you might write,

Caffeine.newBuilder()
    .expireAfter(new Expiry<K, V>() {
      public long expireAfterCreate(K key, V value, long currentTime) {
        var toMidnight = Duration.between(LocalDate.now(), 
            LocalDate.now().plusDays(1).atStartOfDay());
        var toNoon = Duration.between(LocalTime.now(), LocalTime.NOON);
        return toNoon.isNegative() ? toMidnight.toNanos() : toNoon.toNanos();
      }
      public long expireAfterUpdate(K key, V value, 
          long currentTime, long currentDuration) {
        return currentDuration;
      }
      public long expireAfterRead(K key, V value, 
          long currentTime, long currentDuration) {
        return currentDuration;
      }
    }).build();

Using expiration might be overkill for such a simple task. Instead if you wanted to clear the cache then a scheduled task can do that instead, as @alexzander-zharkov suggested.

@Scheduled(cron = "0 0,12 * * *")
public void clear() {
  cache.invalidateAll();
}

As this empties the cache there will be a performance penalty as the entries are loaded anew. Instead you might refresh the cache asynchronously, so that the entries are reloaded without penalizing any callers.

@Scheduled(cron = "0 0,12 * * *")
public void refresh() {
  cache.refreshAll(cache.asMap().keySet());
}
Ben Manes
  • 9,178
  • 3
  • 35
  • 39