Expiry Time @Cacheable Spring Boot

Spring Boot Native cache : How to expire/remove cache data by individual keys/elements

You can expire a single cache entry by using @CacheEvict on a method that takes your cache key. Also, by using Spring's cache and @Cacheable, there's no need for the HashMap code (as that's really just a secondary cache).

Simple Cache
@Service
@CacheConfig(cacheNames = {"userTokens"})
public class UserTokenManager {

private static Logger log = LoggerFactory.getLogger(UserTokenManager.class);

@Cacheable(cacheNames = {"userTokens"})
public String getUserToken(String userName) {
log.info("Fetching user token for: {}", userName);
String token = ""; //replace with call for token
return token;
}

@CacheEvict(cacheNames = {"userTokens"})
public void evictUserToken(String userName) {
log.info("Evicting user token for: {}", userName);
}

@CacheEvict(cacheNames = {"userTokens"}, allEntries = true)
public void evictAll() {
log.info("Evicting all user tokens");
}
}

For example:

  1. getUserToken("Joe") -> no cache, calls API
  2. getUserToken("Alice") -> no cache, calls API
  3. getUserToken("Joe") -> cached
  4. evictUserToken("Joe") -> evicts cache for user "Joe"
  5. getUserToken("Joe") -> no cache, calls API
  6. getUserToken("Alice") -> cached (as it has not been evicted)
  7. evictAll() -> evicts all cache
  8. getUserToken("Joe") -> no cache, calls API
  9. getUserToken("Alice") -> no cache, calls API
TTL-based Cache

If you want your tokens to be cached for a certain amount of time, you'll need another CacheManager besides the native Spring one. There are a variety of cache options that work with Spring's @Cacheable. I'll give an example using Caffeine, a high performance caching library for Java 8. For example, if you know you want to cache a token for 30 minutes, you'll likely want to go with this route.

First, add the following dependencies to your build.gradle (or if using Maven, translate the following and put it in your pom.xml). Note that you'll want to use the latest versions, or the ones that match with your current Spring Boot version.

compile 'org.springframework.boot:spring-boot-starter-cache:2.1.4'
compile 'com.github.ben-manes.caffeine:caffeine:2.7.0'

Once you've added those two dependencies, all you have to do is configure the caffeine spec in your application.properties file:

spring.cache.cache-names=userTokens
spring.cache.caffeine.spec=expireAfterWrite=30m

Change expireAfterWrite=30m to whatever value you'd like tokens to live for. For example, if you wanted 400 seconds, you could change it to expireAfterWrite=400s.

Useful links:

  • Caffeine Spec JavaDoc
  • Spring Boot Supported Cache Providers

Can I schedule Java Spring Cache to expire at the top of each hour like a CRON job?

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


Related Topics



Leave a reply



Submit