javaspring-bootcachingspring-webfluxreactive-programming

Spring WebFlux caching with Caffeine


I am having trouble understanding how to cache Reactive streams using the spring-data-starter-cache library. From tutorials such as https://www.baeldung.com/spring-webflux-cacheable, I see that they make use of either Mono.cache or Caffine.newBuilder to instantiate some type of Map-like cache object that needs to be managed. Seems like the issue stems from @Cacheable only caching the wrapper and not the actual results itself

// application.yml
spring:
  cache:
    type: caffeine
    caffeine:
      spec: maximumSize=1000,expireAfterAccess=3600s


// Code logic
@Cacheable(value = "stuff")
public Mono<String> getInfo(int id) {
    return infoProvider.getInfo(id);
}

What do I need to do to fix this to cache the value emitted by the Mono?

My inital thought is that it could just be a matter of using return infoProvider.getInfo(id).cache(); with some duration, but I was told that this would not work. Why not?


Solution

  • I believe you'll need to build a custom cache using buildAsync.

    Example config

    Please note that I'm using expireAfterWrite because expireAfterAccess resets the expiration timer every time the entry is accessed, which is not very efficient.

    import com.github.benmanes.caffeine.cache.Caffeine;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.caffeine.CaffeineCacheManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.concurrent.TimeUnit;
    
    @Configuration(proxyBeanMethods = false)
    public class CacheConfig {
    
        @Bean
        public CacheManager cacheManager() {
            var cacheManager = new CaffeineCacheManager();
    
            cacheManager.registerCustomCache("stuff",
                    Caffeine.newBuilder()
                            .expireAfterWrite(1, TimeUnit.HOURS)
                            .maximumSize(1000)
                            .buildAsync()); // Note: buildAsync
    
            return cacheManager;
        }
    }