Как работает таймер с истечением срока действия кэша?

Я знаю, что Guava Cache позволяет настраивать отдельные кэши со временем истечения. Делает ли это Guava, используя таймер, который активируется через заданное количество секунд, чтобы сделать кеш недействительным?

У меня есть долгосрочная транзакция. Что бы ни находилось в кеше в начале транзакции, я бы хотел, чтобы оно продолжалось до конца транзакции. Таким образом, даже если во время транзакции истечет количество секунд срока действия кэша, значения, к которым получен доступ из кэша, должны оставаться неизменными до тех пор, пока мы не достигнем конца транзакции. Это возможно в Гуаве?

Спасибо,
Яши

1 ответ

Решение

С какого времени происходит уборка? · Caches Explained · Google / Guava Wiki:

Тайники построены с CacheBuilder не выполняйте очистку и высвобождайте значения "автоматически", или сразу после истечения срока действия, или что-либо в этом роде. Вместо этого он выполняет небольшие объемы обслуживания во время операций записи или во время периодических операций чтения, если записи выполняются редко.

Причина этого заключается в следующем: если мы хотели выполнить Cache Для постоянного обслуживания нам потребуется создать поток, и его операции будут конкурировать с пользовательскими операциями для разделяемых блокировок. Кроме того, некоторые среды ограничивают создание потоков, которые CacheBuilder непригодный в этой среде.

Вместо этого мы ставим выбор в ваши руки. Если ваш кэш высокопроизводительный, вам не нужно беспокоиться о выполнении обслуживания кеша для очистки записей с истекшим сроком действия и тому подобного. Если ваш кэш выполняет запись только в редких случаях, и вы не хотите, чтобы очистка блокировала чтение из кэша, вы можете создать собственный поток обслуживания, который вызывает Cache.cleanUp() через равные промежутки времени.

Если вы хотите запланировать регулярное обслуживание кеша для кеша, в котором редко есть записи, просто запланируйте обслуживание, используя ScheduledExecutorService,

Таким образом, если вы делаете только чтение, вы можете быть хорошим, если вы делаете Cache.cleanUp() непосредственно до и после вашей транзакции, но гарантии до сих пор нет.

Однако вместо того, чтобы пытаться заставить элементы оставаться в кеше, вы можете просто изгнать их в другой кеш / карту, используя removalListener и затем, когда вы читаете, вам сначала нужно проверить кеш, а затем, если его там нет, проверить элементы, выселенные во время длительной транзакции.

Ниже приведен упрощенный пример:

Map<Integer, String> evicted = new ConcurrentHashMap<>();
Cache<Integer, String> cache = CacheBuilder.newBuilder()
        .expireAfterAccess(2, SECONDS)
        .removalListener((RemovalListener<Integer, String>) notification -> 
                evicted.put(notification.getKey(), notification.getValue()))
        .build();
assert evicted.size() == 0 && cache.size() == 0;
cache.put(0, "a");
cache.put(1, "b");
cache.put(2, "c");
assert evicted.size() == 0 && cache.size() == 3;
sleepUninterruptibly(1, SECONDS);
assert evicted.size() == 0 && cache.size() == 3;
cache.put(3, "d");
assert evicted.size() == 0 && cache.size() == 4;
sleepUninterruptibly(1, SECONDS);
cache.cleanUp();
assert evicted.size() == 3 && cache.size() == 1;
Integer key = 2;
String value;
{
    value = cache.getIfPresent(key);
    if (value == null) value = evicted.get(key);
}
assert Objects.equals(value, "c");

Ваш фактический код должен быть условно put в evicted, уборка evicted, управлять несколькими evicted объекты, если вы выполняете долго выполняющиеся транзакции одновременно или используете общий кэш между потоками с другой стратегией вытеснения и т. д., но, надеюсь, это достаточно продемонстрирует идею, чтобы начать работу.

Другие вопросы по тегам