package org.infinispan.distribution.rehash;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.infinispan.test.concurrent.StateSequencerUtil.advanceOnInboundRpc;
import static org.infinispan.test.concurrent.StateSequencerUtil.matchCommand;
import static org.testng.AssertJUnit.fail;
import java.util.concurrent.Future;
import org.infinispan.commands.tx.VersionedPrepareCommand;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.test.MultipleCacheManagersTest;
import org.infinispan.test.concurrent.StateSequencer;
import org.infinispan.test.fwk.CleanupAfterMethod;
import org.infinispan.transaction.LockingMode;
import org.infinispan.transaction.lookup.EmbeddedTransactionManagerLookup;
import org.infinispan.transaction.tm.EmbeddedTransaction;
import org.infinispan.util.ControlledConsistentHashFactory;
import org.testng.annotations.Test;
/**
* Test that if the primary owner crashes while two transactions are in the prepare phase, only one of them will be able
* to commit the transaction.
*
* @author Dan Berindei
* @since 8.0
*/
@Test(groups = "functional", testName = "distribution.rehash.OptimisticPrimaryOwnerCrashDuringPrepareTest")
@CleanupAfterMethod
public class OptimisticPrimaryOwnerCrashDuringPrepareTest extends MultipleCacheManagersTest {
public void testPrimaryOwnerCrash() throws Exception {
// cache 0 is the originator and backup, cache 1 is the primary owner
StateSequencer ss = new StateSequencer();
ss.logicalThread("main", "block_prepare", "crash_primary", "resume_prepare");
tm(0).begin();
cache(0).put("k", "v1");
EmbeddedTransaction tx1 = (EmbeddedTransaction) tm(0).suspend();
tx1.runPrepare();
advanceOnInboundRpc(ss, cache(1), matchCommand(VersionedPrepareCommand.class).build())
.before("block_prepare", "resume_prepare");
Future<EmbeddedTransaction> tx2Future = fork(() -> {
tm(0).begin();
cache(0).put("k", "v2");
EmbeddedTransaction tx2 = (EmbeddedTransaction) tm(0).suspend();
tx2.runPrepare();
return tx2;
});
ss.enter("crash_primary");
killMember(1);
ss.exit("crash_primary");
EmbeddedTransaction tx2 = tx2Future.get(10, SECONDS);
try {
tx2.runCommit(false);
fail("tx2 should not be able to commit");
} catch (Exception e) {
log.tracef(e, "Received expected exception");
}
tx1.runCommit(false);
}
@Override
protected void createCacheManagers() throws Throwable {
ConfigurationBuilder config = new ConfigurationBuilder();
config.clustering().cacheMode(CacheMode.DIST_SYNC);
config.transaction().lockingMode(LockingMode.OPTIMISTIC);
config.clustering().hash().numSegments(1)
.consistentHashFactory(new ControlledConsistentHashFactory(1, 0));
config.transaction().transactionManagerLookup(new EmbeddedTransactionManagerLookup())
.cacheStopTimeout(1, SECONDS);
createCluster(config, 2);
waitForClusterToForm();
}
}