Transaction isolation within a single process
gabuzo Aug 23, 2011 9:50 AMI'm building an application using an Infinispan cache in transactional mode with atomikos. I wrote a small test to check if I get a correct isolation between transactions occuring at the same time. The test consist in performing the following steps:
- populate the cache with a single element.
- Launch two threads concurrently:
- one writer that will get the element, modify it, put it back to the cache and commiting the transaction,
- one reader that will get the element and displays it at various moments to check that, until the writer's transaction is committed, no change is visible from another transaction.
The element stored was a very simple java bean with value and the following implementation of toString() to get additional information:
public String toString() { return "[" + System.identityHashCode(this) + "] id: " + this.id + ", value: " + this.value; }
The writer code is the following:
@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 }
The reader code is the following:
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); }
And the cache is created with the code below.
cacheManager = new DefaultCacheManager(); cacheManager.getDefaultConfiguration().fluent().storeAsBinary(); final Configuration config = new Configuration().fluent().transactionManagerLookup(this.tmLookup).locking() .isolationLevel(IsolationLevel.READ_COMMITTED).build(); this.cacheManager.defineConfiguration("Gruik", config); this.cache = this.cacheManager.getCache("Gruik");
It did expect to have a different object in the reader thread and the writer thread and to have the changes made into the writer not visible until the end of the writer's transaction. However I get the following output:
[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
We clearly see that both threads share the same objects hence we don't have any isolation between distinct transaction.
I find the behavior quite strange and I'm wondering if I didn't miss something in my Infinispan configuration.