package org.infinispan.statetransfer;
import static java.lang.String.valueOf;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertNotNull;
import javax.transaction.Status;
import org.infinispan.Cache;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.container.DataContainer;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.distribution.MagicKey;
import org.infinispan.test.MultipleCacheManagersTest;
import org.infinispan.test.fwk.CleanupAfterMethod;
import org.infinispan.transaction.lookup.EmbeddedTransactionManagerLookup;
import org.infinispan.transaction.tm.EmbeddedTransaction;
import org.infinispan.transaction.tm.EmbeddedTransactionManager;
import org.testng.annotations.Test;
/**
* Checks if the transactions are forward correctly to the new owners
*
* @author Pedro Ruivo
* @since 6.0
*/
@Test(groups = "functional", testName = "statetransfer.TxDuringStateTransferTest")
@CleanupAfterMethod
public class TxDuringStateTransferTest extends MultipleCacheManagersTest {
private static final String INITIAL_VALUE = "v1";
private static final String FINAL_VALUE = "v2";
public void testPut() throws Exception {
performTest(Operation.PUT);
}
public void testRemove() throws Exception {
performTest(Operation.REMOVE);
}
public void testReplace() throws Exception {
performTest(Operation.REPLACE);
}
public void testConditionalPut() throws Exception {
performTest(Operation.CONDITIONAL_PUT);
}
public void testConditionalRemove() throws Exception {
performTest(Operation.CONDITIONAL_REMOVE);
}
public void testConditionalReplace() throws Exception {
performTest(Operation.CONDITIONAL_REPLACE);
}
@Override
protected void createCacheManagers() throws Throwable {
ConfigurationBuilder builder = getDefaultClusteredCacheConfig(CacheMode.DIST_SYNC, true);
builder.transaction()
.transactionManagerLookup(new EmbeddedTransactionManagerLookup())
.useSynchronization(false)
.recovery().disable();
builder.clustering()
.stateTransfer().fetchInMemoryState(true)
.hash().numOwners(3);
createClusteredCaches(4, builder);
}
private void performTest(Operation operation) throws Exception {
assertClusterSize("Wrong number of caches.", 4);
final Object key = new MagicKey(cache(0), cache(1), cache(2));
//init
operation.init(cache(0), key);
final EmbeddedTransactionManager transactionManager = (EmbeddedTransactionManager) tm(0);
transactionManager.begin();
operation.perform(cache(0), key);
final EmbeddedTransaction transaction = transactionManager.getTransaction();
transaction.runPrepare();
assertEquals("Wrong transaction status before killing backup owner.",
Status.STATUS_PREPARED, transaction.getStatus());
//now, we kill cache(1). the transaction is prepared in cache(1) and it should be forward to cache(3)
killMember(1);
assertEquals("Wrong transaction status after killing backup owner.",
Status.STATUS_PREPARED, transaction.getStatus());
transaction.runCommit(false);
for (Cache<Object, Object> cache : caches()) {
//all the caches are owner
operation.check(cache, key, valueOf(address(cache)));
}
}
private enum Operation {
PUT,
REMOVE,
REPLACE,
CONDITIONAL_PUT,
CONDITIONAL_REMOVE,
CONDITIONAL_REPLACE;
public final void init(Cache<Object, Object> cache, Object key) {
if (this != CONDITIONAL_PUT) {
cache.put(key, INITIAL_VALUE);
}
}
public final void perform(Cache<Object, Object> cache, Object key) {
switch (this) {
case PUT:
cache.put(key, FINAL_VALUE);
break;
case REMOVE:
cache.remove(key);
break;
case REPLACE:
cache.replace(key, FINAL_VALUE);
break;
case CONDITIONAL_PUT:
cache.putIfAbsent(key, FINAL_VALUE);
break;
case CONDITIONAL_REMOVE:
cache.remove(key, INITIAL_VALUE);
break;
case CONDITIONAL_REPLACE:
cache.replace(key, INITIAL_VALUE, FINAL_VALUE);
break;
}
}
public final void check(Cache<Object, Object> cache, Object key, String cacheAddress) {
//all the caches are owner. So, check in data container.
DataContainer dataContainer = cache.getAdvancedCache().getDataContainer();
if (this == REMOVE || this == CONDITIONAL_REMOVE) {
assertFalse("Key was not removed in '" + cacheAddress + "'!", dataContainer.containsKey(key));
} else {
InternalCacheEntry entry = dataContainer.get(key);
assertNotNull("Cache '" + cacheAddress + "' does not contains entry!", entry);
assertEquals("Cache '" + cacheAddress + "' has wrong value!", FINAL_VALUE, entry.getValue());
}
}
}
}