Обновление кэширования в GuavaAfterWrite путаница
Я получаю результаты, которые я действительно не понимаю при использовании Guava Caches.
Я реализую один кеш-ключ, который я хочу обновить асинхронно.
Я попадаю в кеш каждую секунду, и я установил refreshAfterWrite на 20 секунд.
Моя функция загрузки/перезагрузки занимает 5 секунд.
Если я распечатаю в начале метода load/reload текущее время - я бы ожидал некоторых результатов, подобных этому:
загрузка вызова началась в 00:00:00
перезагрузка вызова началась в 00:00:25
перезагрузка начинается в 00:00:50
Таким образом, загрузка займет 5 секунд, а следующая запись начнет через 20 секунд после этого (5 + 20 = 25). Эта запись будет происходить через 50 секунд (25 + 5 + 20 = 50) секунд после этого.. etc
Вместо этого я получаю:
загрузка вызова началась в 00:00:00
перезагрузка вызова началась в 00:00:25
перезагрузка начинается в 00:00:30
Это говорит о том, что вторая перезагрузка происходит сразу после завершения первой перезагрузки.
Я думал, что запись произойдет после того, как будущее будет обработано, и поэтому следующая перезагрузка будет запланирована на 20 секунд после этого?
Я нашел ошибку или у меня есть фундаментальное непонимание того, как работает refreshAfterWrite?
Пример кода ниже:
private static SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
public static void main(String[] args) throws ExecutionException, InterruptedException {
final ExecutorService executor = Executors.newFixedThreadPool(3);
final LoadingCache<String, Long> cache = CacheBuilder.newBuilder().maximumSize(1) //
.refreshAfterWrite(20, TimeUnit.SECONDS)//
.build(new CacheLoader<String, Long>() {//
public Long load(String key) {
return getLongRunningProcess("load", key);
}
public ListenableFuture<Long> reload(final String key, Long prevGraph) {
ListenableFutureTask<Long> task = ListenableFutureTask.create(new Callable<Long>() {
public Long call() {
return getLongRunningProcess("reload", key);
}
});
executor.execute(task);
return task;
}
});
while (true) {
Thread.sleep(1000L);
cache.get(CACHE_KEY);
}
}
private static Long getLongRunningProcess(String callType, String key) {
System.out.printf("%s call started at %s\n", callType, format.format(new Date()));
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return counter.getAndIncrement();
}
}
Ответы
Ответ 1
Я думаю, что вы нашли законную ошибку. (Я поддерживаю common.cache
.)
Если я правильно соблюдаю, я считаю, что цепочка событий выглядит следующим образом:
Скажем, получим, что A - это первый get
, который вызывает обновление, и после этого B является первым get
.
- Получить вызовы
scheduleRefresh
, который запускает задачу refresh
в исполнителе. Ссылка на значение позиции заменяется на LoadingValueReference
, а loadAsync
добавляет слушателя, ожидающего завершения перезагрузки.
- Развернутая задача для перезагрузки Get A завершается и получает блокировку.
- Получить B вызовов
scheduleRefresh
. Время доступа еще не обновлено, поэтому оно продолжается и переходит в insertLoadingValueReference
.
- Разбитая задача для перезагрузки Get A обновляет время записи и заменяет ссылку на значение с помощью
StrongValueReference
, так как загрузка завершена. Блокировка отключена.
- Get B определяет, что значение не находится в процессе загрузки, поэтому оно начинает перезагружать новую версию.
(Обновление: зарегистрировано https://code.google.com/p/guava-libraries/issues/detail?id=1211.)