Исключение Gemfire EntryNotFoundException для @CacheEvict

#java #spring #caching #gemfire #spring-data-gemfire

#java #весна #кэширование #gemfire #весна-данные-gemfire

Вопрос:

Короче говоря, когда @CacheEvict вызывается в методе и если ключ для записи не найден, Gemfire выдает исключение EntryNotFoundException .

Теперь подробнее,

У меня есть класс

 class Person {

 String mobile;
 int dept;
 String name;

}
  

У меня есть две области кэша, определенные как personRegion и personByDeptRegion, и служба выглядит следующим образом

 @Service
class PersonServiceImpl {

   @Cacheable(value = "personRegion")
   public Person findByMobile(String mobile) {

      return personRepository.findByMobile(mobile);

   }


   @Cacheable(value = "personByDeptRegion")
   public List<Person> findByDept(int deptCode) {

      return personRepository.findByDept(deptCode);

   }


   @Caching(
      evict = { @CacheEvict(value = "personByDeptRegion", key="#p0.dept"},
      put = { @CachePut(value = "personRegion",key = "#p0.mobile")}

   )
   public Person updatePerson(Person p1) {

      return personRepository.save(p1);

   }

}
  

При вызове updatePerson и если в personByDeptRegion нет записей, это вызовет исключение, которое EntryNotFoundException для ключа 1 (или любого другого кода отдела). Существует очень большая вероятность того, что этот метод будет вызван до вызова методов @Cacheable, и вы хотите избежать этого исключения.
Есть ли какой-либо способ, которым мы могли бы настроить поведение Gemfire, чтобы корректно возвращать, когда ключ не существует для данного региона?.
В качестве альтернативы, я также хотел бы узнать, существует ли лучшая реализация вышеупомянутого сценария с использованием Gemfire в качестве кэша.

Источник данных Gemfire: 1.7.4

Версия Gemfire: v8.2.1

Примечание: Приведенный выше код предназначен только для представления, и у меня есть несколько сервисов с одинаковой проблемой в реальном проекте.

Комментарии:

1. Вау! Вам следует подумать об обновлении вашей версии SDG! 1.3.5.ВЫПУСК? o.O, как вы понимаете 1.8.4.RELEASE , является самой последней версией (на основе GF 8.2.0). 1.7.6.RELEASE — поддерживаемая в настоящее время версия обслуживания (на основе GF 8.1.0). И в настоящее время разрабатывается SDG 1.9, при этом 1.9 M1 уже доступен, а 1.9 GA запланирован на 12 декабря. Вы можете оставаться в курсе, просматривая страницу проекта ЦУР ( projects.spring.io/spring-data-gemfire ) …

2. … а также взглянуть на вики-страницу матрицы совместимости проекта ( github.com/spring-projects/spring-data-gemfire/wiki /… ). Кроме того, вы можете просмотреть сведения о выпуске на странице SD Wiki ( github.com/spring-projects/spring-data-commons/wiki ); смотрите навигационную колонку справа. Текущими версиями являются Gosling (SDG 1.7.6.RELEASE) и Hopper (SDG 1.8.4.RELEASE). Ingalls ( github.com/spring-projects/spring-data-commons/wiki /… ) находится в стадии разработки.

3. Сказав это… Я немного дополню «ответ» на ваш вопрос.

4. Мне очень жаль, Джон, я поставил версию spring boot вместо SDG: (Версия SDG 1.7.4 также обновлена в вопросе. Мы скоро перейдем на spring boot 1.4.1, поэтому я считаю, что это приведет к достижению 1,8 ЦУР 🙂

Ответ №1:

Во-первых, я рекомендую вам использовать аннотации кеширования Spring @Service для компонентов вашего приложения. Слишком часто разработчики включают кэширование в своих репозиториях, что, на мой взгляд, является дурным тоном, особенно если сложные бизнес-правила (или даже дополнительный ввод-вывод; например, вызов веб-службы из компонента service) задействованы до или после взаимодействия с репозиторием (ами), особенно в случаях, когда поведение кэширования не должно бытьзатронуто (или определено).

Я также думаю, что ваше кэширование UC (обновление одного кэша ( personRegion ) при одновременном аннулировании другого ( personByDeptRegion ) при обновлении хранилища данных), следуя a CachePut с a CacheEvict , кажется мне разумным. Тем не менее, я хотел бы отметить, что, по-видимому, предполагаемое использование @Caching аннотации заключается в объединении нескольких аннотаций кэширования одного и того же типа (например, множественных @CacheEvict или множественных @CachePut ), как описано в справочном руководстве core Spring Framework . Тем не менее, ничто не мешает вашему предполагаемому использованию.

Я создал аналогичный тестовый класс здесь, смоделированный по вашему примеру выше, чтобы проверить проблему. Действительно, тестовый пример jonDoeUpdateSuccessful завершается неудачно (с GemFire EntryNotFoundException , показанным ниже), поскольку ни один человек из Department «R amp; D» ранее не кэшировался в « DepartmentPeople » области GemFire до обновления, в отличие от тестового примера janeDoeUpdateSuccessful, который приводит к заполнению кэша до обновления (даже если запись не содержитзначения, что не имеет значения).

 com.gemstone.gemfire.cache.EntryNotFoundException: RESEARCH_DEVELOPMENT
    at com.gemstone.gemfire.internal.cache.AbstractRegionMap.destroy(AbstractRegionMap.java:1435)
  

ПРИМЕЧАНИЕ: в моем тесте GemFire используется как «поставщик кэша», так и система записей (SOR).

Проблема действительно заключается в использовании ЦУР Region.destroy(ключ) в реализации GemfireCache.evict(ключ), а не, и, возможно, более подходящего Region.remove(ключ).

GemfireCache.evict(key) было реализовано с Region.destroy(key) момента создания. Однако Region.remove(key) оно не было введено до версии GemFire версии 5.0. Тем не менее, я не вижу заметной разницы между Region.destroy(key) и Region.remove(key) , кроме EntryNotFoundException брошенного by Region.destroy(key) . По сути, они оба уничтожают локальную запись (как ключ, так и значение), а также распространяют операцию на другие кэши в кластере (при условии, что используется не- LOCAL область видимости).

Итак, я подал SGF-539, чтобы изменить SDG на вызов Region.remove(key) , GemfireCache.evict(key) а не Region.destroy(key) .

Что касается обходного пути, ну, в принципе, вы можете сделать только 2 вещи:

  1. Реструктурируйте свой код и использование @CacheEvict аннотации и / или…
  2. Используйте condition on @CacheEvict .

К сожалению, a condition нельзя указать с использованием типа класса, что-то похожее на условие Spring (в дополнение к SpEL), но этот интерфейс предназначен для другой цели, и @CacheEvict condition атрибут , не принимает тип класса.

На данный момент у меня нет хорошего примера того, как это может работать, поэтому я продвигаюсь вперед по SGF-539.

Вы можете следить за этим тикетом для получения более подробной информации и прогресса.

Извините за неудобства.

-Джон

Комментарии:

1. @Sandheep — У меня отличные новости… Я нашел обходной путь для вас, пока не разрешу SGF-539. Смотрите мой пример тестового класса обновления ( github.com/jxblum/spring-gemfire-tests/blob/master/src/test /… ) для получения более подробной информации.

2. Обратите внимание, ключ к «обходному пути» заключается в следующем … github.com/jxblum/spring-gemfire-tests/blob/master/src/test/… (именно то изменение в ЦУР, которое необходимо внести) … 😉

3. Отличный Джон :). Большое вам спасибо за создание заявки и особенно за немедленное решение проблемы. Я как бы застрял с этим. Протестирует обходной путь и сообщит вам.

4. Привет, Сандхип- 1 последнее обновление. Я разрешил / исправил SGF-539. Он был зафиксирован 1.7.x (для Gosling 1.7.7.RELEASE), 1.8.x (для Хоппера 1.8.5.RELEASE) apache-geode и master (для Ingalls 1.9 RC1). К сожалению, только что вышли версии обслуживания SDG 1.7.6 и 1.8.4, а 1.7.7 / 1.8.5 не запланированы на некоторое время. 1.9 RC1 запланирован на конец ноября с окончательным 1.9 GA 12/12. Обходной путь работал безупречно в моем примере тестового класса. Если у вас возникнут проблемы, дайте мне знать. Приветствия!

5. На месте 🙂 Я попробовал обходной путь, и он работает нормально. Однако у меня есть класс GemfireConfig, аннотированный с помощью @Configuration . Когда я добавил CacheManager в качестве компонента в этот класс, первый раз после запуска приложения выдал ту же ошибку, и последующие вызовы работали (подтверждено, что в обоих случаях кэш списка был пуст). Затем я переместил определение компонента в выделенный класс, а GemfireConfig имеет другие определения региона. Похоже, это устраняет проблему с первым запуском. Я не совсем уверен в причине. Но, похоже, это как-то связано с порядком создания компонента.