Кэширование
Модуль для создания кешей на основе Caffeine или Redis с помощью аннотаций в декларативном стиле, так и использование их императивном стиле.
Caffeine¶
Реализация на основе библиотеки Caffeine для кэша внутри памяти приложения.
Подключение¶
Зависимость build.gradle
:
Модуль:
Зависимость build.gradle.kts
:
Модуль:
Конфигурация¶
Пример полной конфигурации для mycache.config
кэша, параметры описаны в классе CaffeineCacheConfig
(указаны примеры значений или значения по умолчанию):
mycache {
config {
expireAfterWrite = "10s" //(1)!
expireAfterAccess = "10s" //(2)!
initialSize = 10 //(3)!
maximumSize = 100000 //(4)!
}
}
- Время по истечении которого значение для ключа будет удалено, отчитывается после добавления значения (по умолчанию отсутвует)
- Время по истечении которого значение для ключа будет удалено, отчитывается после операции чтения (по умолчанию отсутвует)
- Начальный размер кэша (помогает избежать расширения кэша в случае активного набухания) (по умолчанию отсутвует)
- Максимальный размер кэша (При достижении границы или чуть ранее будет исключать из кэша наименее актуальные значения) (по умолчанию
100000
)
mycache:
config:
expireAfterWrite: "10s" #(1)!
expireAfterAccess: "10s" #(2)!
initialSize: 10 #(3)!
maximumSize: 100000 #(4)!
- Время по истечении которого значение для ключа будет удалено, отчитывается после добавления значения (по умолчанию отсутвует)
- Время по истечении которого значение для ключа будет удалено, отчитывается после операции чтения (по умолчанию отсутвует)
- Начальный размер кэша (помогает избежать расширения кэша в случае активного набухания) (по умолчанию отсутвует)
- Максимальный размер кэша (При достижении границы или чуть ранее будет исключать из кэша наименее актуальные значения) (по умолчанию
100000
)
Redis¶
Реализация на основе базы данных в памяти Redis и драйвера подключения Lettuce.
Подключение¶
Зависимость build.gradle
:
Модуль:
Зависимость build.gradle.kts
:
Модуль:
Конфигурация¶
Требуется отдельно сконфигурировать Lettuce драйвер для подключения к Redis. Используется одно подключение для всех кешей.
Пример полной конфигурации для lettuce драйвера, параметры описаны в классе LettuceConfig
(указаны примеры значений или значения по умолчанию):
lettuce {
uri = "redis://locahost:6379" //(1)!
user = "admin" //(2)!
password = "12345" //(3)!
database = 0 //(4)!
protocol = "REP3" //(5)!
socketTimeout = "10s" //(6)!
commandTimeout = "60s" //(7)!
}
- URI для подключения к Redis (обязательный)
- Имя пользователя для подключения (по умолчанию отсутвует)
- Пароль пользователя для подключения (по умолчанию отсутвует)
- Номер базы для подключения (по умолчанию отсутвует)
- Протокол для подключения
- Таймаут времени подключения
- Таймаут времени выполнения команды
lettuce:
uri: "redis://locahost:6379" #(1)!
user: "admin" #(2)!
password: "12345" #(3)!
database: 0 #(4)!
protocol: "REP3" #(5)!
socketTimeout: "10s" #(6)!
commandTimeout: "60s" #(7)!
- URI для подключения к Redis (обязательный)
- Имя пользователя для подключения (по умолчанию отсутвует)
- Пароль пользователя для подключения (по умолчанию отсутвует)
- Номер базы для подключения (по умолчанию отсутвует)
- Протокол для подключения
- Таймаут времени подключения
- Таймаут времени выполнения команды
Конфигурации Redis кэша настраивает именно поведение конкретного кэша.
Пример полной конфигурации для mycache.config
кэша, параметры описаны в классе RedisCacheConfig
(указаны примеры значений):
mycache {
config {
expireAfterWrite = "10s" //(1)!
expireAfterAccess = "10s" //(2)!
keyPrefix = "mykey" //(3)!
}
}
- При записи устанавливает время expiration
- При чтении устанавливает время expiration
- Префикс ключа в определенном кеше для избежания коллизий ключе в рамках Redis базы данных, может быть пустой строкой тогда ключи будут без префикса (обязательный)
mycache:
config:
expireAfterWrite: "10s" #(1)!
expireAfterAccess: "10s" #(2)!
keyPrefix: "mykey" //(3)!
- При записи устанавливает время expiration
- При чтении устанавливает время expiration
- Префикс ключа в определенном кеше для избежания коллизий ключе в рамках Redis базы данных, может быть пустой строкой тогда ключи будут без префикса (обязательный)
Использование¶
Для создания кеша потребуется зарегистрировать типизированный @Cache
контракт.
Интерфейс контракта должен наследоваться только от предоставляемых Kora'ой реализаций: CaffeineCache
/ RedisCache
.
Для такого @Cache
будет создана и добавлена в граф реализация, ее можно будет использовать для внедрения зависимостей.
Для регистрации @Cache
и указания конфига требуется проаннотировать аннотацией @Cache
где аргумент value
означает полный путь к конфига.
Императивный подход¶
Кеши доступны для внедрения как зависимости по интерфейсу и могут использовать вкупе с декларативными операциями.
Реализация CaffeineCache
предоставляет базовые контракты интерфейса Cache
для синхронных операций,
а RedisCache
предоставляет как Cache
так и AsyncCache
для асинхронных операций с CompletionStage
сигнатурами.
Интерфейсы предоставляют операции получения, удаления, обновления, пакетных операций и тп. Также реализации кешей могут предоставлять специфичные для себя контракты.
Декларативный подход¶
Все примеры использования аспектов будут подразумевать реализацию кэша выше.
Получение¶
Для кэширования и получения значения из кэша для метода get() следует проаннотировать его аннотацией @Cacheable
.
Ключ для кэша составляет из аргументов метода, порядок аргументов имеет значение, в данном случае он будет составляться из значения arg1
.
Сохранение¶
Для добавления значений в кэш через метод put() следует проаннотировать его аннотацией @CachePut
.
Метод проаннотированный @CachePut
будет вызван и его значение положено в кэш определенный в value.
Ключ для кэша составляет из аргументов метода, порядок аргументов имеет значение, в данном случае он будет составляться из значения arg1
.
Удаление¶
Для удаления значения по ключу из кэша через метод evict() следует проаннотировать его аннотацией @CacheInvalidate
.
Метод проаннотированный @CacheInvalidate
будет вызван и затем по ключу для кэша определенного в value будут удалены значения по ключу.
Ключ для кэша составляет из аргументов метода, порядок аргументов имеет значение, в данном случае он будет составляться из значения arg1
.
Полное удаление¶
Для удаления всех значений из кэша через метод evictAll() следует проаннотировать его аннотацией @CacheInvalidate
и указать параметр invalidateAll = true.
Метод проаннотированный @CacheInvalidate
будет вызван и затем будут удалены все из кэша определенного в value.
Композитный кэш¶
В случае если у вас есть несколько кешей то требуется подключить оба модуля и указать соответствующее количество аннотаций над методом.
А сам проаннотированный класс так:
Порядок вызова аспектов соответствует порядку аннотаций над методом, сверху внизу.
Ключ¶
В случае если ключ кэша представляет собой 1 аргумент, то требуется зарегистрировать Cache
с сигнатурой соответствующей типам ключа и значения.
Преобразование¶
В случае если аргумент не может быть преобразован в ключ кеша, то реализация кеша затребует соответствующий преобразователь
с интерфейсом CacheKeyMapper
, в случае если аргументов для ключа будет 2 то потребуется CacheKeyMapper2
и так далее.
Такой преобразователь можно также предоставить в ручную с помощью аннотации @Mapping
,
пример преобразования сложного объекта в простой ключ кеша:
@Component
public class SomeService {
public record UserContext(String userId, String traceId) { }
public static final class UserContextMapping implements CacheKeyMapper<String, UserContext> {
@Nonnull
@Override
public String map(UserContext arg) {
return arg.userId();
}
}
@Mapping(UserContextMapping.class)
@Cacheable(MyCache.class)
public String get(UserContext context) {
// do something
}
}
Композитный ключ¶
В случае если ключ кэша представляет собой N аргументов, то требуется зарегистрировать Cache
с использованием
собственного класса который бы описывал такой ключ.
Пример для Cache
где композитный ключ состоит из 2 элементов:
Предполагается создавать собственный record
класс который бы описывал композитный ключ.
Если используется RedisCache
то подразумевается что по умолчанию все аргументы композитного ключа будут не null
,
либо потребуется использовать собственный преобразователь ключа.
Порядок аргументов¶
В случае если метод принимает аргументы которые хочется исключить из композитного ключа,
либо же порядок аргументов не соответствует порядку аргументов конструктора композитного ключа,
следует использовать атрибут аннотации parameters
и определить какие именно аргументы метода использовать и в каком порядке.
Подгружаемый кэш¶
Библиотека предоставляет компонент для построения сущности, которая объединяет операции GET и PUT, без использования аспектов - LoadableCache
@Cache("mycache.config")
public interface MyCache extends CaffeineCache<String, String> { }
@KoraApp
public interface Application : CaffeineCacheModule {
default LoadableCache<String, String> loadableCache(MyCache cache, SomeService someService) {
return cache.asLoadable(someService::loadEntity);
}
}
Сигнатуры¶
Доступные сигнатуры для методов которые поддерживают аннотации из коробки:
Класс не должен быть final
, чтобы аспекты работали.
Под T
подразумевается тип возвращаемого значения.
T myMethod()
Optional<T> myMethod()
CompletionStage<T> myMethod()
CompletionStageMono<T> myMethod()
Project Reactor (надо подключить зависимость)
Класс должен быть open
, чтобы аспекты работали.
Под T
подразумевается тип возвращаемого значения, либо T?
, либо Unit
.
myMethod(): T
suspend myMethod(): T
Kotlin Coroutine (надо подключить зависимость какimplementation
)