Можно ли выключить кэш персистентного контекста при работе двух микросервисов с базой?
Что проверяют
Различаете ли вы кэш первого уровня, то есть сам persistence context, и кэш второго уровня Hibernate. Сценарий с двумя сервисами на одной базе нужен, чтобы услышать, как вы боретесь с устаревшими данными и конкурентной записью. Следом спрашивают про @Version и оптимистичную блокировку.
Как отвечать
Persistence context выключить нельзя: это обязательная часть JPA. EntityManager (в Hibernate это Session) гарантирует, что одна строка в рамках контекста представлена одним объектом, и на этом держатся dirty checking и отложенный flush. Управлять можно временем жизни контекста и его содержимым:
- короткий контекст: в Spring он по умолчанию живёт в пределах транзакции, поэтому транзакции делают короткими. Заодно выключают
spring.jpa.open-in-view, который растягивает контекст на весь HTTP-запрос; - точечно:
em.refresh(entity)перечитывает строку из базы,em.detach()иem.clear()выбрасывают объекты из контекста; - без контекста:
StatelessSessionв Hibernate не хранит объекты, не делает dirty checking и ленивую загрузку. Подходит для пакетной обработки; - второй уровень: вот его можно отключить целиком (
hibernate.cache.use_second_level_cache=false,jakarta.persistence.sharedCache.mode=NONE) или обойти для запроса хинтомCacheRetrieveMode.BYPASS. Если чужой сервис пишет в те же таблицы, опасен именно общий кэш второго уровня.
Главное: отключение кэша не решает конкурентную запись. Два сервиса затирают изменения друг друга, пока нет @Version (при конфликте падает OptimisticLockException) или LockModeType.PESSIMISTIC_WRITE, то есть SELECT ... FOR UPDATE. Стоит добавить, что общая база у двух сервисов сама по себе спорное решение.
Где спотыкаются
Путают уровни и предлагают выключить persistence context свойством use_second_level_cache. Это свойство про другой кэш.
Думают, что JPQL-запрос всегда вернёт свежие данные. Запрос действительно пойдёт в базу, но если сущность уже есть в контексте, вернётся тот же объект со старым состоянием.
Лечат устаревание вызовом clear() после каждой операции. Работать всё начинает медленнее, а гонка при записи никуда не делась.