Кофеин java кеш - как сначала загрузить новое значение, а затем вернуть новое вместо старого, если срок действия refreshAfterWrite истек - PullRequest
0 голосов
/ 30 марта 2020

Я пытаюсь использовать кофеин кеш и у меня проблема:

Допустим, кеш пуст, и я запрашиваю значение, он использует загрузчик и загружает новое значение в кеш, через 2 дня Я передаю запрос на то же значение, и сначала получаю значение OLD , затем refre sh запускается в отдельном потоке, и новое значение загружается, если загрузка возможна.

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

То, что я хочу заархивировать, - сначала попытаться обновить sh и, если возможно, сначала вернуть новое значение, если что-то пойдет не так, только потом вернуть старое. Как я могу заархивировать это? Как реализовать простую, перспективную, аккуратную реализацию без обходных путей? Это было бы здорово!

Редактировать: Будет ли это решение работать правильно?

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;

1 Ответ

2 голосов
/ 30 марта 2020

Я не думаю, что использование CacheWriter здесь необходимо. Вместо этого проверьте метаданные, чтобы определить, требуется ли refre sh.

Самым простым может быть запуск запланированной задачи, которая запускает refre sh на ранней стадии. Например,

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;
  }
});
...