2016-10-07 3 views
1

У меня есть кэш Guava, созданный с помощью expireAfterWrite(20, TimeUnit.MINUTES). Кэш также инициализируется десятками записей при создании. Когда значения истекают, клиент должен вызывать внешнюю службу и обновлять кеш.Вероятностное раннее истечение с помощью кэша Guava

Проблема: все инициализированные значения истекают одновременно и через 20 минут, и клиент будет обращаться к службе десятки раз почти в одном экземпляре.

Я не хочу, чтобы это произошло. Чтобы этого избежать - одна идея заключается в probabilistically expire the entries чуть раньше, чем TTL, так что услуга не будет сильно ударяться одновременно.

К сожалению, я не вижу возможности делать это с помощью кеша Guava - по крайней мере, ничего не вышло из Wiki или Javadoc. Есть ли другая библиотека? Я пытаюсь избежать написания собственной реализации кеша для этой цели.

+3

Guava использует ордер O (1), поэтому это невозможно. Это несколько смягчается также с помощью 'refreshAfterWrite()', так что активно используемые записи перезагружаются без блокировки других читателей. В противном случае его слева на ваш собственный код, чтобы пакет, перегреться, использовать вторичный слой и т. Д., Чтобы создать более мощную политику. –

ответ

1

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

public static <K, V> void staggerCacheExpiration(
    Cache<K, V> cache, long maxExpiration, TimeUnit unit, ScheduledExecutorService scheduler) { 
    for (Entry<K, V> e : cache.asMap().entrySet()) { 
    long delay = ThreadLocalRandom.current().nextLong(0, maxExpiration); 
    scheduler.schedule(() -> cache.put(e.getKey(), e.getValue()), delay, unit); 
    } 
} 

Критическая проблема с любым подходом, опирающимся на Cache сами вы будете в конечный итоге привести к срабатыванию (потенциально дорого) обновлять только тогда, когда вызывающему нужно значение. Может быть лучше не использовать expireAfterWrite() или refreshAfterWrite() и вместо этого запускать выделенный поток, который отвечает за обновление каждого ключа в последовательности. Благодаря тому, что один поток обновляет все ключи, вы, естественно, избегаете любых горячих точек, где одновременно обновляются несколько ключей, а также избегайте блокировки любых потоков, полагающихся на значения из кеша.

Как Бен Манес предлагает вам предпочесть инкапсулировать ваш Cache в ваш собственный тип, чтобы любое поведение, которое вы выбираете, не подвергалось воздействию ваших абонентов.

Смежные вопросы