У нас есть Java-приложение, которое использует MySQL, Hibernate (3.5.1-Final) и EHcache (1.2.3) для нашего кэша 2-го уровня.
Наш уровень изоляции hibernate.properties - изоляция с фиксацией чтения = 2
# 2-Read committed isolation
hibernate.connection.isolation=2
При большом количестве одновременных транзакций мы видим проблему, при которой определенные коллекции (ассоциации БД) при загрузке генерируют исключение ObjectNotFoundException, и кажется, что кэш 2-го уровня возвращает старую копию этой коллекции.
У нас есть много различных типов транзакций, которые обращаются к этой коллекции (только чтение), и только пара, которая будет добавлять / удалять элементы из нее.
Мы не видим эту проблему при загрузке одной транзакции или даже при умеренной загрузке транзакции (10-20 одновременных подключений).
Например, у нас есть сущность персонажа:
@Entity
@Table(name = "CHARACTERS")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Character extends AbstractCharacter implements Serializable {
...
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@OneToMany(mappedBy = "character", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<CharacterItem> items;
Мы надлежащим образом поддерживаем граф объектов при удалении сущностей, удаляя их из коллекции, в которой они содержатся, и вызывая session.delete ().
character.getItems().remove(characterItem);
session.delete(characterItem);
Мы попытались изменить элементы набора; CacheConcurrencyStrategy от:
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<CharacterItem> items;
К
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<CharacterItem> items;
Без удачи.
Мы не используем блокировки базы данных, вместо этого мы используем оптимистический контроль параллелизма , чтобы перехватывать и повторять конфликтующие транзакции.
На данный момент мы можем видеть только 2 решения:
Попытайтесь поймать ObjectNotFoundException и попытаться интеллектуально выселить коллекцию (хотя в исключении не хватает контекста)
Используйте аннотацию @ NotFound (action = NotFoundAction.IGNORE) для коллекции элементов, которая будет игнорировать и не выбрасывать исключение ObjectNotFoundException (но у нас есть опасения относительно как это работает с кэшем 2-го уровня и убедитесь, что он смотрит на правильные данные).
Хотелось бы, чтобы был @NotFound (action = NotFoundAction.EVICT_2ND_LEVEL_CACHE_RELOAD), где он вытеснил бы этот объект из кэша и попытался бы перезагрузить коллекцию.
Мы также можем попытаться изменить FetchyType с LAZY на EAGER, но я хочу попытаться понять проблему и выбрать лучшее решение, которое обеспечит согласованность данных в наших транзакциях при высоком параллелизме.