#scala #caching #caffeine-cache
Вопрос:
Пример использования приложения Scala:
У нас есть основанный на Scala модуль, который считывает данные из глобального кэша (Redis) и сохраняет их в локальном кэше(Scaffeine). Поскольку мы хотим, чтобы эти данные обновлялись асинхронно, мы используем LoadingCache с длительностью обновления, установленной для окна обновления 2 секунды.
Вопрос:
Нам нужно установить другое время истечения срока действия при установке значений в локальном кэше в зависимости от того, присутствует ли ключ в redis (глобальном кэше) или нет.
например, если ключ отсутствует в глобальном кэше, мы хотим сохранить тот же ключ в локальном кэше со значением по умолчанию и окном обновления, установленным на 5 минут. Если ключ присутствует в глобальном кэше, мы хотим сохранить его в локальном кэше с фактическим значением и окном обновления, установленным на 30 минут.
Пример кода
object LocalCache extends App {
// data being stored in the cache
class DataObject(data: String) {
override def toString: String = {
"[ 'data': '" this.data "' ]"
}
}
// loader helper
private def loaderHelper(key: Int): Future[DataObject] = {
// this method will replace to read the data from Redis Cache
// for now, returns different values per key
if (key == 1) Future.successful(new DataObject("LOADER_HELPER_1"))
else if (key == 2) Future.successful(new DataObject("LOADER_HELPER_2"))
else Future.successful(new DataObject("LOADER_HELPER"))
}
// async loader
private def loader(key: Int): DataObject = {
Try {
Await.result(loaderHelper(key), 1.seconds)
} match {
case Success(result) =>
result
case Failure(exception: Exception) =>
val temp: DataObject = new DataObject("LOADER")
temp
}
}
// initCache
private def initCache(maximumSize: Int): LoadingCache[Int, DataObject] =
Scaffeine()
.recordStats()
.expireAfterWrite(2.second)
.maximumSize(maximumSize)
.build(loader)
// operations on the cache.
val cache: LoadingCache[Int, DataObject] = initCache(maximumSize = 500)
cache.put(1, new DataObject("foo"))
cache.put(2, new DataObject("hoo"))
println("sleeping for 3 secn")
Thread.sleep(3000)
println(cache.getIfPresent(1).toString)
println(cache.getIfPresent(2).toString)
println(cache.get(3).toString)
println("sleeping for 10 secn")
Thread.sleep(10000)
println("waking up from 10 sec sleep")
println(cache.get(1).toString)
println(cache.get(2).toString)
println(cache.get(3).toString)
println("nCache Stats: " cache.stats())
}
Я вижу множество пользовательских политик.policy, которые можно использовать для перезаписи политик expiryAfter (expiryAfterWrite/Обновление/Доступ), но ничего нельзя найти для политик refreshAterWrite, которые асинхронно обновляют данные. Любая помощь будет ощутима.
P.S. Я очень новичок в работе над Scala, а также в изучении лесов.
Ответ №1:
К сожалению, обновление переменных пока не поддерживается. Существует открытая проблема, связанная с предоставлением этой функции.
На данный момент срок действия может быть настраиваемым для каждой записи, но автоматическое обновление исправлено. Обновление вручную может быть вызвано LoadingCache.refresh(key)
, если вы хотите управлять им самостоятельно. Например, вы можете периодически перебирать записи (через asMap()
представление) и обновлять их вручную на основе пользовательских критериев.
Кэш AsyncLoadingCache может быть полезен вместо блокировки в будущем в вашем загрузчике кэша. Кэш вернет будущее в полете, не сделает его пригодным для использования до тех пор, пока значение не материализуется, и удалит его в случае сбоя. Обратите внимание, что synchronous()
представление очень полезно для асинхронных кэшей для доступа к большему количеству операций.
После тестирования вы можете обнаружить, что поддельный тикер Гуавы полезен для имитации времени.
Комментарии:
1. Спасибо @Ben за помощь.