Gemfire EntryNotFoundException для @CacheEvict

Короче говоря, когда @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 в качестве кеша.

Spring Data Gemfire: 1.7.4

Версия Gemfire: v8.2.1

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


person Sandheep    schedule 03.10.2016    source источник
comment
Ух ты! Вам следует подумать об обновлении вашей версии 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 декабря. Вы можете быть в курсе, просматривая страницу проекта SDG (projects.spring.io/spring-data- гемфайр) ...   -  person John Blum    schedule 03.10.2016
comment
... а также взгляните на вики-страницу матрицы совместимости проектов (github.com/spring-projects/spring-data-gemfire/wiki/). Кроме того, вы можете просмотреть сведения о выпуске на странице SD Wiki (github.com/spring -projects/spring-data-commons/wiki); см. столбец Nav справа. Текущие выпуски: Gosling (SDG 1.7.6.RELEASE) и Hopper (SDG 1.8.4.RELEASE). Ингаллс (github.com/spring-projects/spring- data-commons/wiki/).   -  person John Blum    schedule 03.10.2016
comment
Сказав это... Я дам ответ на ваш вопрос через некоторое время.   -  person John Blum    schedule 03.10.2016
comment
Мне очень жаль, Джон, я поставил версию весенней загрузки вместо SDG :( Версия SDG - 1.7.4. Также мы обновили вопрос. Скоро мы перейдем на весеннюю загрузку 1.4.1, поэтому я верю, что это принесет в 1.8 ЦУР :)   -  person Sandheep    schedule 04.10.2016


Ответы (1)


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

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

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

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

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

На самом деле проблема заключается в том, что SDG использует Region.destroy(key) в GemfireCache.evict(key), а не , и, возможно, более уместно, Region.remove(key).

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

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

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

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

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

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

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

Приносим извинения за доставленные неудобства.

-Джон

person John Blum    schedule 04.10.2016
comment
@Sandheep - у меня отличные новости ... Я нашел для вас обходной путь, пока не решу SGF-539. См. мой пример тестового класса обновления (github.com/jxblum/spring-gemfire-tests/blob/master/src/test/) для получения более подробной информации. - person John Blum; 04.10.2016
comment
Обратите внимание, что ключ к обходному пути заключается в следующем... .java#L157" rel="nofollow noreferrer">github.com/jxblum/spring-gemfire-tests/blob/master/src/test/ (именно то изменение в SDG, которое необходимо внести). . ;-) - person John Blum; 04.10.2016
comment
Превосходный Джон :). Большое спасибо за создание тикета и особенно за немедленное обходное решение. Как-то застрял с этим. Проверим обходной путь и сообщим вам. - person Sandheep; 04.10.2016
comment
Привет Sandheep - 1 последнее обновление. Я решил/исправил SGF-539. Он был зафиксирован в 1.7.x (для Gosling 1.7.7.RELEASE), 1.8.x (для Hopper 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 декабря. Обходной путь работал безупречно в моем примере тестового класса. Если у вас есть проблемы, дайте мне знать. Ваше здоровье! - person John Blum; 04.10.2016
comment
Точно :) Я попробовал обходной путь, и он отлично работает. Одна вещь, однако, у меня есть класс GemfireConfig, аннотированный @Configuration. Когда я добавил CacheManager в качестве bean-компонента в этот класс, в первый раз после запуска приложения возникла та же ошибка, и последующие вызовы работали (подтверждено, что в обоих случаях кеш списка был пуст). Затем я переместил определение bean-компонента в выделенный класс, а GemfireConfig имеет определения других регионов. Кажется, это решает проблему с первым запуском. Я не совсем уверен в причине. Но, похоже, это как-то связано с порядком создания бина. - person Sandheep; 04.10.2016