Search code examples
javaspringspring-cachejcachecaffeine

Why I have cache misses in Service using Spring Cache


I have configured my cache as follows:

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean(name = "caffeineCachingProvider")
    public CachingProvider caffeineCachingProvider() {
        return Caching.getCachingProvider("com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider");
    }

    @Bean(name = "caffeineCacheManager")
    public JCacheCacheManager getSpringCacheManager() {
        CacheManager cacheManager = caffeineCachingProvider().getCacheManager();
        CaffeineConfiguration<String, List<Product>> caffeineConfiguration = new CaffeineConfiguration<>();
        caffeineConfiguration.setExpiryPolicyFactory(FactoryBuilder.factoryOf(new AccessedExpiryPolicy(new Duration(TimeUnit.MINUTES, 60))));
        caffeineConfiguration.setCopierFactory(Copier::identity);
        cacheManager.createCache("informerCache", caffeineConfiguration);
        return new JCacheCacheManager(cacheManager);
    }
}

Also I have the @Service that uses it in following way:

@Service
public class InformerService {

    @CacheResult(cacheName = "informerCache")
    public List<Product> getProducts(@CacheKey String category, @CacheKey String countrySign, @CacheKey long townId) throws Exception {
        Thread.sleep(5000);
        // do some work
    }
}

So I have the next behavior.

  1. When I'm calling the service method first time it takes 5 seconds then doing some work as expected.
  2. Calling method the second time with same parameters - > caching works -> returns result immediately
  3. Calling the third time with same parameters again results in Thread.sleep

And all over again.

How to solve this ? Is that the issue about proxying ? What did I miss ?


Solution

  • As discussed in the comments, this was a bug in the JCache adapter. Thank you for letting me know about this problem. I released version 2.1.0 which includes this fix. That release also includes friendlier initial settings for CaffeineConfiguration which you identified in another post.

    While the core library is heavily tested, the JCache adapters relied too heavily on the JSR's TCK (test compatibility kit). Unfortunately that test suite isn't very effective, so I added tests to help avoid these types of mistakes in the future.

    This issue only occurred in JCache because its version of expiration is not supported by Caffeine's core library. Caffeine prefers to use an O(1) design that eagerly cleans up expired entries by using fixed durations. JCache uses per-entry lazy expiration and the specification authors assume a capacity constraint is used to eventually discard expired entries. I added a warning to the documentation about this feature, as it could be error prone. While none of the other JCache implementations go beyond this, a pending task is to decide on a mechanism to help mitigate this JCache design flaw.

    Thanks again for reporting this issue. As always, feel free to reach out if you have any other issues or feedback to share.