Кеш выталкивает на один из нескольких ключей

В моем приложении у меня есть несколько кэшируемых методов с несколькими ключами:

@Cacheable(cacheNames = "valueCodes", key = "{#value, #fieldId, #projectId}")
@Cacheable(cacheNames = "fieldNames", key = "{#field, #value, #projectId}")
@Cacheable(cacheNames = "qi", key = "{#langCode, #question, #projectId}")
@Cacheable(cacheNames = "fieldCodes", key = "{#name, #projectId}")

Теперь я хочу метод cachevict, который очищает все кеши, где совпадает только ключ #projectId, который является UUID:

@CacheEvict(value = {"valueCodes", "fieldCodes", "qi"; "fieldCodes"}, key = "#projectId")

Я читал в этой статье, что это невозможно и что

Только регулярное выражение ключа аннотации выселения, совпадающее с более чем одним элементом в каждом из имен cacheNames

Я не совсем уверен, что они подразумевают под этим, но я думаю, это как-то связано с использованием регулярных выражений в SpEL.

Поэтому я начал думать о соединении моих ключей в один ключ:

@Cacheable(cacheNames="cahceName", key="concat(#projectId).concat(#otherKey)")

и использование регулярного выражения для сопоставления всех ключей с идентификатором проекта, за которым следует подстановочный знак. Но я не мог найти способ сделать это.

Возможно ли то, что я пытаюсь достичь? Если да, то как мне это сделать?

2 ответа

Решение

Вместо того, чтобы использовать аннотации для поиска ключа по частичному ключу, я создал компонент, который управляет ключами для меня

  1. Я удалил все аннотации @CacheEvict.
  2. Создан новый сервис, который будет управлять выселением для всех наших кешей

    public interface CacheEvictionService {
        /**
         * Adds the provided key to a global list of keys that we'll need later for eviction
         *
         * @param key the cached key for any entry
         */
        void addKeyToList(String key);
    
        /**
         * Find keys that contain the partial key
         *
         * @param partialKey the cached partial key for an entry
         * @return List of matching keys
         */
        List<String> findKeyByPartialKey(String partialKey);
    
        /**
         * Evicts the cache and key for an entry matching the provided key
         *
         * @param key the key of the entry you want to evict
         */
        void evict(String key);
    
    }
    
    @Service
    public class CacheEvictionServiceImpl implements CacheEvictionService {
        LinkedHashSet<String> cachedKeys = new LinkedHashSet<>();
    
        @Override
        public void addKeyToList(String key) {
            this.cachedKeys.add(key);
        }
    
        @Override
        public List<String> findKeyByPartialKey(String partialKey) {
            List<String> foundKeys = new ArrayList<>();
            for (String cachedKey : this.cachedKeys) {
                if (cachedKey.contains(partialKey)) {
                    foundKeys.add(cachedKey);
                }
            }
            return foundKeys;
        }
    
        @Override
        @CacheEvict(value = {"valueCodes", "fieldCodes", "qi", "fieldNames", "fieldsByType"}, key = "#key")
        public void evict(String key) {
            this.cachedKeys.remove(key);
        }
    }
    
  3. Вместо использования нескольких ключей объедините различные ключи в одну строку

    @Cacheable(cacheNames = "valueCodes", key = "#value.concat(#fieldId).concat(#projectId)")
    
  4. Отправляйте ключ в сервис каждый раз, когда что-то кешируется

    cacheEvictionService.addKeyToList(StringUtils.join(value, fieldId, projectId));
    
  5. Цикл по каждому существующему ключу, который содержит идентификатор проекта (или любой другой ключ)

    for (String cachedKey : cacheEvictionService.findKeyByPartialKey(projectId)) {
        cacheEvictionService.evict(cachedKey);
    }
    

Возможно ли то, что я пытаюсь достичь? Если да, то как мне это сделать?

То, что вы любите делать, невозможно.

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

Дважды проверьте, имеет ли смысл кэшировать все фрагменты, которые принадлежат проекту, отдельно. Если все должно быть выселено вместе, возможно, оно используется вместе все время. В качестве альтернативы, например, сохранить ConcurrentHashMap в качестве значения в кеше, который содержит различные компоненты, принадлежащие проекту.

Подробнее об этом см. Вопрос: что является лучшим вариантом для многоуровневого внутрипроцессного кэша?

Вероятно, имеет смысл отбросить аннотации и использовать кеш напрямую. Варианты с аннотациями ограничены.

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