1

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

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

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

我要存档的是-尝试先刷新并在可能的情况下先返回新值,如果出现问题,则仅返回旧值。我该如何存档?如何在没有变通方法的情况下实现简单、面向未来、整洁的实现?那将是真棒!

编辑:这个解决方案能正常工作吗?

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;
4

1 回答 1

3

我不认为在这里使用 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;
  }
});
于 2020-03-30T18:10:54.927 回答