我正在围绕 Infinispan 缓存和 Atomikos 事务管理器构建应用程序。我发现事务隔离不适用于在同一 JVM 上的两个不同线程中打开的事务。
使用以下代码实例化缓存:
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");
Withthis.tmLookup
是org.infinispan.transaction.lookup.TransactionManagerLookup
返回配置 Atomikos 事务管理器的简单实现。
我通过使用单个值填充缓存来设置一个小测试,然后在两个线程中启动一个读取器和一个写入器,每个线程都在一个单独的事务中。基本上,写入器将获取存储在缓存中的值,更改值并将其保存到缓存中。另一方面,使用 get 读取并在不同阶段显示值:在 writer 执行任何更改之前,在 writer 更改 pojo 之后,在 writer 保存更新的 pojo 之后,最后在 writer 事务提交之后.
编写器代码是:
@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 没有事务 ID:
INFO atomikos - addParticipant [...]
对比
INFO atomikos ehcache-txid=0 - addParticipant