Caffeine java 缓存 - 如果 refreshAfterWrite 过期,如何先加载新值然后 return 新值而不是旧值

Caffeine java cache - how to load new value first and then return new one instead of old value, if refreshAfterWrite expired

我正在尝试使用咖啡因缓存但遇到了问题:

假设缓存为空,我查询一个值,它使用加载程序并将一个新值加载到缓存中,2 天过去后,我查询相同的值并得到 OLD value first 然后在单独的线程上启动刷新,如果可以加载,则加载新值。

Caffeine.newBuilder()
        .refreshAfterWrite(5, TimeUnit.MINUTES)
        .expireAfterWrite(3, TimeUnit.DAYS)
        .build(loader);

我想要存档的是 - 尝试先刷新,如果可能的话先 return 新值,如果出现问题才 return 旧值。我该如何存档?如何在没有变通办法的情况下实施简单的、面向未来的、整洁的实施?那太棒了!

编辑:此解决方案能否正常工作?

boolean needsSyncRefresh = cache.policy().expireAfterWrite()
                .flatMap(stringBigDecimalExpiration -> stringBigDecimalExpiration.ageOf(key))
                .filter(age -> age.toMinutes() < 0 || age.toMinutes() > REFRESH_AFTER_MINUTES)
                .isPresent();

V value = cache.get(key);

if (needsSyncRefresh) {
    return cache.asMap().computeIfPresent(key, (k, oldValue) -> {
        if (oldValue.equals(value)) {
            try {
                return loader(key);
            } catch (Exception e) {
                //handle error
            }
        }
        return oldValue;
    });
}
return value;

我认为这里没有必要使用 CacheWriter。而是检查元数据以确定是否需要刷新。

最简单的方法可能是 运行 提前触发刷新的计划任务。例如,

var policy = cache.policy().expireAfterWrite().get();
for (var key : cache.asMap().keySet()) {
  boolean refresh = policy.ageOf(key)
      .filter(age -> age.toDays() >= 2)
      .isPresent();
  if (refresh) {
    cache.refresh(key);
  }
}

一个稍微复杂的版本在检索时通过检查检索到的值是否过时并执行新的计算来执行此操作,例如

V value = cache.get(key, this::loadValue);

var policy = cache.policy().expireAfterWrite().get();
boolean refresh = policy.ageOf(key)
    .filter(age -> age.toDays() >= 2)
    .isAbsent();
if (!refresh) {
  return value;
}
return cache.asMap().compute((key, oldValue) -> {
  if ((oldValue != null) && (oldValue != value)) {
    return oldValue; // reloaded by another thread
  }
  try {
    return loadValue(key);
  } catch (Exception e) {
    logger.error("Failed to load {}", key, e);
    return oldValue;
  }
});