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!");
}
}
}