4

I am trying to write an application to cache which reloads every few seconds. I decided to use spring boot Caffeine and got a sample application too. But when I am specifying refreshAfterWrite property, it throws exception: refreshAfterWrite requires a LoadingCache

spring:
    cache:
        cache-names: instruments, directory
        caffeine:
            spec: maximumSize=500, expireAfterAccess=30s, refreshAfterWrite=30s

To resolve this I provide Loading Cache Bean, but cache stopped working altogether:

@Bean
    public CacheLoader<Object, Object> cacheLoader() {
        return string -> {
            System.out.println("string = " + string);
            return string;
        };
    }

    @Bean
    public LoadingCache<Object, Object> loader(CacheLoader<Object, Object> cacheLoader) {
        return Caffeine.newBuilder()
                .refreshAfterWrite(1, TimeUnit.SECONDS)
                .build(cacheLoader);
    }

Do we have some simple way for reload to work?

krmanish007
  • 6,749
  • 16
  • 58
  • 100
  • Spring Cache abstraction provides implementers with only `get(key, function)`, but that means the function could change. To refresh automatically, Caffeine expects a function upfront to call but that can't be provided by Spring's abstraction. Their recommendation is that their abstraction is for the simple / common case and, rather than enhance it to support more complex scenarios, users should prefer using the native APIs instead. – Ben Manes Dec 06 '18 at 20:42
  • Are you using this `Cache` with the cache abstraction? If so, what is the use case to refresh the cache after 30s since the intent is that the method invocation is the one that is responsible to fetch the data if necessary? If you just add an expiration, the cache abstraction will call the method you've annotated the next time you try to access the data and the cache entry expired. – Stephane Nicoll Dec 10 '18 at 09:07
  • Hi @StephaneNicoll, the use-case is to reload the data change every 30 sec, and keep it cached, so that when we receive the request after 30 sec, it should pick up to date from cache directly. Otherwise every 30sec, the cache will expire and the next request will have to load the data again, and so that call will take time. The main idea to keep the data precompiled and avoid runtime call. – krmanish007 Dec 10 '18 at 11:05
  • You shouldn't be using the cache abstraction then. If you're loading the cache yourself, you are duplicating what the cache abstraction already does. When you flag a method `@Cacheable` you're teaching the infrastructure what it should call when an entry is missing. – Stephane Nicoll Dec 10 '18 at 13:13
  • our requirement too is to load the data when the cache is missing (exactly what `@Cacheable` does), and whatever we have already cached, should keep refreshing every 30 sec. So, its a mix of 2 different patterns, which is supported in `Caffeine` but in its native implementation. – krmanish007 Dec 10 '18 at 13:42
  • It's a mix of 2 different patterns as you say, the latter completely replacing the very purpose of the former. I am with Ben you should not use the declarative model in your case. – Stephane Nicoll Dec 10 '18 at 14:27
  • Yes @StephaneNicoll and so have started using native Caffeine for use-case. – krmanish007 Dec 10 '18 at 15:09
  • @krmanish007, Can you share the implemantation or an example. I am trying to achieve the same here but finding it difficult to do so. It would be of great help – Einstein_AB May 04 '20 at 10:12
  • I used caffeine directly and it was working fine. You can refer the example here https://www.baeldung.com/java-caching-caffeine – krmanish007 May 04 '20 at 16:45
  • @BenManes "Their recommendation is that … users should prefer using the native APIs instead". Thanks for that! Got a citation in case I need to argue this to my team? (Yes, I realize this is years later. ) – Cody Casterline Feb 25 '22 at 02:15
  • @CodyCasterline Stephane is a Spring core developer and just above said the abstraction isn’t ideal here. The citation would be some comment he made in some issue somewhere for why he rejected requests for more advanced concepts in their abstraction. – Ben Manes Feb 25 '22 at 02:20

1 Answers1

4

To conclude here, using the LoadingCache feature of Caffeine with Spring's cache abstraction does not make much sense since they share a lot of features.

@Cacheable typically provide a way to mark a method to retrieve an element that is not present in the cache yet. LoadingCache achieves the same scenario, requiring you to provide a handle that can load a missing element by id.

If you absolutely need to use a LoadingCache, I'd inject the Cache in your code and interact with it programmatically.

Stephane Nicoll
  • 31,977
  • 9
  • 97
  • 89
  • 4
    I think this is a legitimate bug/enhancement on Spring Boot side. Spring Boot already supports "expireAfterWrite" abstraction. There is no reason not to support "refreshAfterWrite" the same way. Under the hood of abstraction, it's a different way to call Caffeine API. "refreshAfterWrite" needs a Caffeine cache loader (and is currently missing). The abstraction should be "translating" the very same user method that is being annotated by @Cacheable into the cache loader for the underlying Caffeine cache. In that sense, it is exactly the kind of "abstraction" Spring Boot aims to provide. – q3769 Aug 24 '21 at 17:58