Spring cache / jsr107: аргумент списка / коллекции как часть ключа

У меня есть служба, которая вызывает внешнюю систему, чтобы получить какие-то объекты по их внешнему идентификатору, а также отправить их обратно для обновления. Вместо того, чтобы извлекать объекты по одному, существуют методы более общего назначения:

public interface ExternalSystem {
    List<ExternalDTO> getObjects(List<String> externalIds);

    void updateObjects(List<ExternalDTO> updates);
}

Я хотел бы поместить кеш поверх вызовов ExternalSystem, потому что они довольно дороги.

В реализации сервиса я могу просто поставить аннотации spring:

@Cacheable("cache-external")
List<ExternalDTO> getObjects(List<String> externalIds) {} 

@CacheEvict(cacheNames="cache-external", allEntries=true)
void updateObjects(List<ExternalDTO> updates);

Однако такой кеш будет вести себя очень плохо, если у меня будет много пересечений между externalIds, т.е.

  1. Вызов # 1 getObjects ([1,2,3,4]) -> кеш, помещенный клавишей [1,2,3,4]
  2. Вызов # 2 getObjects ([1,2,3,4,5]) -> кеш, помещенный ключом [1,2,3,4,5]
  3. Вызов # 3 getObjects ([6,7,8,9]) -> кеш, помещенный ключом [6,7,8,9]
  4. Вызовите # 4 updateObjects (1) -> удалите все кеши, кроме третьего кеша. не содержит 3

Итак, вопрос в том, как реализовать настраиваемую стратегию (я предполагаю, что это невозможно сделать из коробки), которая вытеснит только те записи, которые действительно должны быть исключены, и сделает ключи таким образом, чтобы пересекающиеся объекты извлекались из кеша. ?

Upd. Я нашел два похожих вопроса:

  1. spring-cache-abstraction-with-multi-value-questions
  2. using- Spring-cache-on-methods-that-take-an-array-or-collection
  3. Spring-cacheable-methods-with-lists

Upd2. Вот что-то похожее на то, что я хочу, за исключением того, что я помещаю в кеш пары String и ExternalDTO для каждого элемента в коллекции. уровень-кеширования-элемента- список в список


person Vadim Kirilchuk    schedule 20.05.2016    source источник


Ответы (2)


AFAIK это невозможно с аннотациями. Вы можете использовать императивный API, который содержит необходимые вам массовые операции, например Cache.getAll(keySet) и Cache.removeAll(keySet).

person cruftex    schedule 20.05.2016
comment
Да, и я уверен, что уже должны быть некоторые индивидуальные реализации. Вопрос не только в удалении, смотрите прикрепленные вопросы в Upd. - person Vadim Kirilchuk; 20.05.2016
comment
Просто любопытно: сколько экземпляров ваше приложение обычно запрашивает одновременно? Если вы не можете сделать это с помощью массового запроса, но один за другим, не сможете ли вы выполнить требования к максимальной задержке со стороны своего клиента? Если вы выберете индивидуальное решение: вы профилировали все свое приложение, и эта оптимизация окажет наибольшее влияние? Просто спрашиваю ;) - person cruftex; 20.05.2016
comment
Это хороший вопрос, но даже если это выполнимо в моем случае, в других случаях это может быть невыполнимо. Итак, вопрос более общий, чем просто мой случай. Итак, я ищу какое-то многоразовое решение для таких случаев. Если не найду, напишу свой и выложу сюда :) - person Vadim Kirilchuk; 20.05.2016

Для меня с этой конфигурацией все работало нормально. Это запутанная версия моего кода.

@Cacheable(cacheNames = "test", key = "#p0")
public List<String> getTestFunction(List<String> someIds) {
getTestFunction(Arrays.asList("A","B","C"));
2020-04-02 15:12:35.492 TRACE 18040 --- [Test worker] o.s.cache.interceptor.CacheInterceptor   : Computed cache key '[A, B, C]' for operation Builder[public java.util.List org.Main.getTestFunction(java.util.List)] caches=[test] | key='#p0' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'

Вы видите, что это конкатенированные строки

... Computed cache key '[A, B, C]' ...

Моя настройка: /resources/ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <cache name="test"
           maxBytesLocalHeap="1M"
           timeToLiveSeconds="300"/>
</ehcache>

gradle.build

plugins {
    id "org.springframework.boot" version "2.2.4.RELEASE"
    ....
}
dependencies {
    implementation "org.springframework.boot:spring-boot-starter-cache"
    implementation "org.ehcache:ehcache:3.8.1"
    ...
}

person Koroslak    schedule 02.04.2020
comment
что такое # p0? ... - person Fernando; 07.01.2021
comment
Я просто погуглил, что такое # p0, и это просто значение параметра 1, которое является ссылкой в ​​шестнадцатеричном формате. Вы используете шестнадцатеричную ссылку в качестве ключа, что я считаю неуместным. - person Fernando; 07.01.2021
comment
@Fernando, ты прав, это значение 1-го параметра. 1. что такое шестнадцатеричная ссылка? 2. почему это неуместно? - person Koroslak; 08.01.2021