Is it possible to set a different specification per cache using caffeine in spring boot?

14,221

Solution 1

This is your only chance:

@Bean
public CaffeineCache cacheA() {
    return new CaffeineCache("CACHE_A",
            Caffeine.newBuilder()
                    .expireAfterAccess(1, TimeUnit.DAYS)
                    .build());
}

@Bean
public CaffeineCache cacheB() {
    return new CaffeineCache("CACHE_B",
            Caffeine.newBuilder()
                    .expireAfterWrite(7, TimeUnit.DAYS)
                    .recordStats()
                    .build());
}

Just expose your custom caches as beans. They are automatically added to the CaffeineCacheManager.

Solution 2

I config multiple cache manager like this

    @Bean
    public CacheManager template() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager(CACHE_TEMPLATE);
        cacheManager.setCaffeine(caffeineCacheBuilder(this.settings.getCacheExpiredInMinutes()));
        return cacheManager;
    }

    @Bean
    public CacheManager daily() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager(CACHE_TEMPLATE);
        cacheManager.setCaffeine(caffeineCacheBuilder(24 * 60));
        return cacheManager;
    }

And use the cache normally

    @Cacheable(cacheManager = "template")
    @Override
    public ArrayList<FmdModel> getData(String arg) {
        return ....;
    }

Update

It look like the above code has a big mistake. So I change to

@Configuration
@Data
@Slf4j
@ConfigurationProperties(prefix = "caching")
public class AppCacheConfig {


    //This cache spec is load from `application.yml` file
    // @ConfigurationProperties(prefix = "caching")
    private Map<String, CacheSpec> specs;

    @Bean
    public CacheManager cacheManager(Ticker ticker) {
        SimpleCacheManager manager = new SimpleCacheManager();
        if (specs != null) {
            List<CaffeineCache> caches = specs.entrySet().stream()
                    .map(entry -> buildCache(entry.getKey(), entry.getValue(), ticker)).collect(Collectors.toList());
            manager.setCaches(caches);
        }
        return manager;
    }

    private CaffeineCache buildCache(String name, CacheSpec cacheSpec, Ticker ticker) {
        log.info("Cache {} specified timeout of {} min, max of {}", name, cacheSpec.getTimeout(), cacheSpec.getMax());
        final Caffeine<Object, Object> caffeineBuilder = Caffeine.newBuilder()
                .expireAfterWrite(cacheSpec.getTimeout(), TimeUnit.MINUTES).maximumSize(cacheSpec.getMax())
                .ticker(ticker);
        return new CaffeineCache(name, caffeineBuilder.build());
    }

    @Bean
    public Ticker ticker() {
        return Ticker.systemTicker();
    }
}

This AppCacheConfig class allow you to define many cache spec as you prefer. And you can define cache spec in application.yml file

caching:
  specs:
    template:
      timeout: 10 #15 minutes
      max: 10_000
    daily:
      timeout: 1440 #1 day
      max: 10_000
    weekly:
      timeout: 10080 #7 days
      max: 10_000
    ...:
      timeout: ... #in minutes
      max:

But still, this class has a limitation that we can only set timeout and max size only. because of CacheSpec class

@Data
public class CacheSpec {

    private Integer timeout;
    private Integer max = 200;

}

Therefore, If you like to add more config parameters, you are to add more parameters on CacheSpec class and set the Cache configuration on AppCacheConfig.buildCache function.
Hope this help!

Solution 3

I converted my initial PR into a separate tiny project.

To start using it just add the latest dependency from Maven Central:

<dependency>
    <groupId>io.github.stepio.coffee-boots</groupId>
    <artifactId>coffee-boots</artifactId>
    <version>2.0.0</version>
</dependency>

Format of properties is the following:

coffee-boots.cache.spec.myCache=maximumSize=100000,expireAfterWrite=1m

If no specific configuration is defined, CacheManager defaults to Spring's behavior.

Share:
14,221
David
Author by

David

Updated on June 17, 2022

Comments

  • David
    David about 2 years

    I have a simple sprint boot application using spring boot 1.5.11.RELEASE with @EnableCaching on the Application Configuration class.

    pom.xml

     <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-cache</artifactId>
     </dependency>
     <dependency>
                <groupId>com.github.ben-manes.caffeine</groupId>
                <artifactId>caffeine</artifactId>
     </dependency>
    

    application.properties

    spring.cache.type=caffeine
    spring.cache.cache-names=cache-a,cache-b
    spring.cache.caffeine.spec=maximumSize=100, expireAfterWrite=1d
    

    Question

    My question is simple, how can one specify a different size/expiration per cache. E.g. perhaps it's acceptable for cache-a to be valid for 1 day. But cache-b might be ok for 1 week. The specification on a caffeine cache appears to be global to the CacheManager rather than Cache. Am I missing something? Perhaps there is a more suitable provider for my use case?