package org.infinispan.distribution.groups; import java.util.Map; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import org.infinispan.transaction.WriteSkewException; import org.infinispan.util.concurrent.IsolationLevel; import org.testng.AssertJUnit; import org.testng.annotations.Test; /** * It tests the grouping advanced interface for transactional caches with write-skew check enabled. * * @author Pedro Ruivo * @since 7.0 */ @Test(groups = "functional", testName = "distribution.groups.WriteSkewGetGroupKeysTest") public class WriteSkewGetGroupKeysTest extends TransactionalGetGroupKeysTest { @Override public Object[] factory() { return new Object[] { new WriteSkewGetGroupKeysTest(TestCacheFactory.PRIMARY_OWNER).totalOrder(false), new WriteSkewGetGroupKeysTest(TestCacheFactory.PRIMARY_OWNER).totalOrder(true), new WriteSkewGetGroupKeysTest(TestCacheFactory.BACKUP_OWNER).totalOrder(false), new WriteSkewGetGroupKeysTest(TestCacheFactory.BACKUP_OWNER).totalOrder(true), new WriteSkewGetGroupKeysTest(TestCacheFactory.NON_OWNER).totalOrder(false), new WriteSkewGetGroupKeysTest(TestCacheFactory.NON_OWNER).totalOrder(true), }; } public WriteSkewGetGroupKeysTest() { super(null); } public WriteSkewGetGroupKeysTest(TestCacheFactory factory) { super(factory); isolationLevel = IsolationLevel.REPEATABLE_READ; } public void testRemoveGroupWithConcurrentConflictingUpdate() throws Exception{ final TestCache testCache = createTestCacheAndReset(GROUP, this.caches()); initCache(testCache.primaryOwner); final TransactionManager tm = tm(testCache.testCache); tm.begin(); Map<GroupKey, String> groupKeySet = testCache.testCache.getGroup(GROUP); Map<GroupKey, String> expectedGroupSet = createMap(0, 10); final Transaction tx = tm.suspend(); AssertJUnit.assertEquals(expectedGroupSet, groupKeySet); testCache.primaryOwner.put(key(1), value(-1)); tm.resume(tx); try { testCache.testCache.removeGroup(GROUP); groupKeySet = testCache.testCache.getGroup(GROUP); expectedGroupSet.clear(); AssertJUnit.assertEquals(expectedGroupSet, groupKeySet); assertCommitFail(tm); //write skew should abort the transaction } catch (WriteSkewException e) { // On non-owner, the second retrieval of keys within the group will find out that one of the entries // has different value and will throw WSE tm.rollback(); } groupKeySet = testCache.testCache.getGroup(GROUP); expectedGroupSet = createMap(0, 10); expectedGroupSet.put(key(1), value(-1)); AssertJUnit.assertEquals(expectedGroupSet, groupKeySet); } public void testRemoveGroupWithConcurrentAdd() throws Exception{ final TestCache testCache = createTestCacheAndReset(GROUP, this.caches()); initCache(testCache.primaryOwner); final TransactionManager tm = tm(testCache.testCache); tm.begin(); Map<GroupKey, String> groupKeySet = testCache.testCache.getGroup(GROUP); Map<GroupKey, String> expectedGroupSet = createMap(0, 10); final Transaction tx = tm.suspend(); AssertJUnit.assertEquals(expectedGroupSet, groupKeySet); testCache.primaryOwner.put(key(11), value(11)); tm.resume(tx); testCache.testCache.removeGroup(GROUP); groupKeySet = testCache.testCache.getGroup(GROUP); expectedGroupSet.clear(); AssertJUnit.assertEquals(expectedGroupSet, groupKeySet); assertCommitOk(tm); //write skew should *not* abort the transaction groupKeySet = testCache.testCache.getGroup(GROUP); AssertJUnit.assertEquals(expectedGroupSet, groupKeySet); } public void testRemoveGroupWithConcurrentConflictingRemove() throws Exception{ final TestCache testCache = createTestCacheAndReset(GROUP, this.caches()); initCache(testCache.primaryOwner); final TransactionManager tm = tm(testCache.testCache); tm.begin(); Map<GroupKey, String> groupKeySet = testCache.testCache.getGroup(GROUP); Map<GroupKey, String> expectedGroupSet = createMap(0, 10); final Transaction tx = tm.suspend(); AssertJUnit.assertEquals(expectedGroupSet, groupKeySet); testCache.primaryOwner.remove(key(9)); tm.resume(tx); testCache.testCache.removeGroup(GROUP); groupKeySet = testCache.testCache.getGroup(GROUP); expectedGroupSet.clear(); AssertJUnit.assertEquals(expectedGroupSet, groupKeySet); assertCommitFail(tm); //write skew should *not* abort the transaction groupKeySet = testCache.testCache.getGroup(GROUP); expectedGroupSet = createMap(0, 9); AssertJUnit.assertEquals(expectedGroupSet, groupKeySet); } public void testRemoveGroupWithConcurrentRemove() throws Exception{ final TestCache testCache = createTestCacheAndReset(GROUP, this.caches()); initCache(testCache.primaryOwner); final TransactionManager tm = tm(testCache.testCache); tm.begin(); Map<GroupKey, String> groupKeySet = testCache.testCache.getGroup(GROUP); Map<GroupKey, String> expectedGroupSet = createMap(0, 10); final Transaction tx = tm.suspend(); AssertJUnit.assertEquals(expectedGroupSet, groupKeySet); testCache.primaryOwner.put(key(11), value(11)); testCache.primaryOwner.put(key(12), value(12)); testCache.primaryOwner.remove(key(12)); tm.resume(tx); testCache.testCache.removeGroup(GROUP); groupKeySet = testCache.testCache.getGroup(GROUP); expectedGroupSet.clear(); AssertJUnit.assertEquals(expectedGroupSet, groupKeySet); assertCommitOk(tm); //write skew should *not* abort the transaction groupKeySet = testCache.testCache.getGroup(GROUP); expectedGroupSet.clear(); AssertJUnit.assertEquals(expectedGroupSet, groupKeySet); } private static void assertCommitFail(TransactionManager tm) throws SystemException { try { tm.commit(); AssertJUnit.fail("Commit should fail!"); } catch (RollbackException | HeuristicMixedException | HeuristicRollbackException e) { //ignored, it is expected } } private static void assertCommitOk(TransactionManager tm) throws SystemException { try { tm.commit(); } catch (RollbackException | HeuristicMixedException | HeuristicRollbackException e) { AssertJUnit.fail("Commit should fail!"); } } }