New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to serve the old (expired) value in case of error? #699
Comments
The guava style of returning if the refresh completed on the triggering thread was recently implemented in #688. Can you try master (snapshot or jitpack) to see if that fits your needs? The caller would need to return a completed future by overriding asyncReload. |
Thanks @ben-manes for your responsiveness. I've just tried with
|
Thanks @mickaeltr. Can you write an isolated unit test for me to debug with? That would avoid any gafs on my side by testing something different than what you're doing. |
If it reloads asynchronously then it is fire-and-forget. Guava has the same except by default it runs on the calling thread, public ListenableFuture<V> reload(K key, V oldValue) throws Exception {
checkNotNull(key);
checkNotNull(oldValue);
return Futures.immediateFuture(load(key));
} In Caffeine we have If you want the same behavior as Guava's then you would override caffeine/caffeine/src/main/java/com/github/benmanes/caffeine/cache/CacheLoader.java Lines 192 to 205 in d9f132c
|
I created reproduction tests:
I'll try later to override the |
It looks like jitpack did not replace the Spring dependency so (in my IDE) it resolves to v2. I switched to Sonatype's repository for 3.1.0-SNAPSHOT. That passes the |
Thanks a lot! 🙌 I confirm it should work starting from version 3.1.0, with the following configuration: import java.lang.reflect.Method;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKey;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
private static final Duration CACHE_DURATION = Duration.ofSeconds(1);
@Bean
@Override
public CacheManager cacheManager() {
var cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder().refreshAfterWrite(CACHE_DURATION));
cacheManager.setCacheLoader(new CacheLoader<>() {
@Override
public Object load(@NonNull Object key) throws Exception {
var cacheKey = (CacheKey) key;
return cacheKey.method.invoke(cacheKey.target, cacheKey.params);
}
@Override
@NonNull
public CompletableFuture<Object> asyncReload(
@NonNull Object key,
@NonNull Object oldValue,
@NonNull Executor executor
) {
try {
return CompletableFuture.completedFuture(load(key));
} catch (Exception e) {
return CompletableFuture.failedFuture(e);
}
}
});
return cacheManager;
}
@Bean
@Override
public KeyGenerator keyGenerator() {
return CacheKey::new;
}
private static class CacheKey extends SimpleKey {
private final Object target;
private final Method method;
private final Object[] params;
private CacheKey(Object target, Method method, Object... params) {
super(params);
this.target = target;
this.method = method;
this.params = params;
}
}
} |
Released in 3.1.0 |
Hello, I am working on a project using Caffeine (2.9) with Spring Boot.
I'd like to implement a behaviour similar to the
stale-if-error
Cache-Control
HTTP header:The question was already raised on StackOverflow and got an answer that I could adapt to Caffeine. However it seems to me that it's not exactly behaving accordingly. As far as I could see, after the cache expiration, the old value is always returned on the first hit (then the refreshed value on the second hit).
Do you think what I am trying to achieve is possible or would it be a new feature? Thanks
Here is what I've done:
The text was updated successfully, but these errors were encountered: