Я создаю приложение на основе кэша Infinispan и менеджера транзакций Atomikos.Я обнаружил, что изоляция транзакции не работает для транзакций, открытых в двух разных потоках на одной и той же виртуальной машине Java.
Кэш-память создается с использованием следующего кода:
cacheManager = new DefaultCacheManager();
final Configuration config = new Configuration().fluent().transactionManagerLookup(this.tmLookup).recovery().locking()
.isolationLevel(IsolationLevel.READ_COMMITTED).build();
this.cacheManager.defineConfiguration("Gruik", config);
this.cache = this.cacheManager.getCache("Gruik");
С this.tmLookup
простая реализация org.infinispan.transaction.lookup.TransactionManagerLookup
, возвращающая сконфигурированный менеджер транзакций Atomikos.
Я настроил небольшой тест, заполнив кэш одним значением, и запускаю в двух потоках читатель и пишущий модуль, каждый в отдельности.сделка.По сути, писатель получит значение, хранящееся в кеше, изменит значение и сохранит его в кеше.С другой стороны, чтение с получением и отображением значения на разных этапах: перед любым изменением, выполненным писателем, после того, как писатель изменил pojo, после того, как писатель сохранил обновленное pojo и, наконец, после фиксации транзакции писателя..
Код записывающего устройства:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void performTrans() throws InterruptedException, BrokenBarrierException {
LOGGER.info("Wait to start");
pBarrier.await(); // 1
final Pojo entity = cache.get(KEY);
LOGGER.info("Start entity: {}", entity);
pBarrier.await(); // 2
entity.setValue(entity.getValue() + 42);
LOGGER.info("Entity changed wait for reader");
pBarrier.await(); // 3
cache.put(KEY, entity);
LOGGER.info("Entity saved wait for reader");
pBarrier.await(); // 4
}
Код считывателя:
public void performTrans() throws InterruptedException, BrokenBarrierException {
LOGGER.info("Wait to start");
pBarrier.await(); // 1
final Pojo entity = cache.get(KEY);
LOGGER.info("Start entity: {}", entity);
pBarrier.await(); // 2
LOGGER.info("Wait writer to make changes");
pBarrier.await(); // 3
LOGGER.info("After change: {}", entity);
pBarrier.await(); // 4
Pojo newEntity = cache.get(KEY);
LOGGER.info("After save: {}", newEntity);
pBarrier.await(); // 5
newEntity = cache.get(KEY);
LOGGER.info("After transaction end: {}", newEntity);
}
Для отслеживания сущностей, возвращаемых кешем, я реализовал Pojo toString()
вот так:
public String toString() {
return "[" + System.identityHashCode(this) + "] id: " + this.id + ", value: " + this.value;
}
Поскольку кэш настроен для изоляции, я ожидал, что между читателем и писателем будут разные экземпляры pojo, и что только изменение будет видно только после транзакции писателя.было зафиксировано.
Однако я получил следующий вывод:
[Reader] - Wait to start
[Writer] - Wait to start
[Writer] - Start entity: [19682788] id: 1, value: 666
[Reader] - Start entity: [19682788] id: 1, value: 666
[Reader] - Wait writer to make changes
[Writer] - Entity changed wait for reader
[Reader] - After change: [19682788] id: 1, value: 708
[Writer] - Entity saved wait for reader
[Reader] - After save: [19682788] id: 1, value: 708
[Reader] - After transaction end: [19682788] id: 1, value: 708
Таким образом, в основном, кеш работает как хэш-карта, поскольку он возвращает один и тот же экземпляр pojo для обоих потоков.
Вопрос : я что-то упустил в конфигурации или в ожидаемом поведении?
Я довольноуверен, что менеджер транзакций работает так, как я могу получать сообщения журнала от Atomikos, указывающие на начало отдельных транзакций как на читателе, так и на писателе.
Однако я попробовал тот же тест, используя Ehcache вместо Infinispan, и получил ожидаемые результаты.Сравнивая журналы между двумя тестами, я обнаружил похожие сообщения, единственное очевидное различие заключается в отсутствии идентификатора транзакции для Infinispan:
INFO atomikos - addParticipant [...]
против
INFO atomikos ehcache-txid=0 - addParticipant