package org.infinispan.container.versioning;
import static org.jgroups.util.Util.assertNotNull;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.fail;
import javax.transaction.RollbackException;
import javax.transaction.Transaction;
import org.infinispan.Cache;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.distribution.MagicKey;
import org.infinispan.distribution.ch.ConsistentHash;
import org.infinispan.remoting.transport.Address;
import org.infinispan.test.MultipleCacheManagersTest;
import org.infinispan.test.TestingUtil;
import org.infinispan.test.fwk.CleanupAfterMethod;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.transaction.LockingMode;
import org.infinispan.util.concurrent.IsolationLevel;
import org.testng.annotations.Test;
@Test(testName = "container.versioning.VersionedDistStateTransferTest", groups = "functional")
@CleanupAfterMethod
public class VersionedDistStateTransferTest extends MultipleCacheManagersTest {
ConfigurationBuilder builder;
@Override
protected void createCacheManagers() throws Throwable {
builder = TestCacheManagerFactory.getDefaultCacheConfiguration(true);
builder.clustering().cacheMode(CacheMode.DIST_SYNC).l1().disable()
.locking().isolationLevel(IsolationLevel.REPEATABLE_READ)
.transaction().lockingMode(LockingMode.OPTIMISTIC);
amendConfig(builder);
createCluster(builder, 4);
waitForClusterToForm();
}
protected void amendConfig(ConfigurationBuilder builder) {
}
public void testStateTransfer() throws Exception {
Cache<Object, Object> cache0 = cache(0);
Cache<Object, Object> cache1 = cache(1);
Cache<Object, Object> cache2 = cache(2);
Cache<Object, Object> cache3 = cache(3);
int NUM_KEYS = 20;
MagicKey[] keys = new MagicKey[NUM_KEYS];
String[] values = new String[NUM_KEYS];
for (int i = 0; i < NUM_KEYS; i++) {
// Put the entries on the cache that will be killed
keys[i] = new MagicKey("key" + i, cache3);
values[i] = "v" + i;
cache0.put(keys[i], values[i]);
}
// no state transfer per se, but we check that the initial data is ok
checkStateTransfer(keys, values);
Transaction[] txs = new Transaction[NUM_KEYS];
for (int i = 0; i < NUM_KEYS; i++) {
int cacheIndex = i % 3;
tm(cacheIndex).begin(); {
assertEquals(values[i], cache(cacheIndex).get(keys[i]));
}
txs[i] = tm(cacheIndex).suspend();
}
log.debugf("Starting joiner");
addClusterEnabledCacheManager(builder);
Cache<Object, Object> cache4 = cache(4);
log.debugf("Joiner started, checking transferred data");
checkStateTransfer(keys, values);
log.debugf("Stopping cache %s", cache3);
manager(3).stop();
// Eliminate the dead cache from the caches collection, cache4 now becomes cache(3)
cacheManagers.remove(3);
TestingUtil.waitForNoRebalance(caches());
log.debugf("Leaver stopped, checking transferred data");
checkStateTransfer(keys, values);
// Cause a write skew
for (int i = 0; i < NUM_KEYS; i++) {
cache4.put(keys[i], "new " + values[i]);
}
for (int i = 0; i < NUM_KEYS; i++) {
int cacheIndex = i % 3;
log.tracef("Expecting a write skew failure for key %s on cache %s", keys[i], cache(cacheIndex));
tm(cacheIndex).resume(txs[i]); {
cache(cacheIndex).put(keys[i], "new new " + values[i]);
}
try {
tm(cacheIndex).commit();
fail("The write skew check should have failed!");
} catch (RollbackException expected) {
// Expected
}
}
for (int cacheIndex = 0; cacheIndex < 4; cacheIndex++) {
for (int i = 0; i < NUM_KEYS; i++) {
assertEquals("Wrong value found on cache " + cache(cacheIndex),
"new " + values[i], cache(cacheIndex).get(keys[i]));
}
}
}
private void checkStateTransfer(MagicKey[] keys, String[] values) {
for (Cache<Object, Object> c: caches()) {
for (int i = 0; i < keys.length; i++) {
assertEquals("Wrong value found on cache " + c, values[i], c.get(keys[i]));
checkVersion(c, keys[i]);
}
}
}
private void checkVersion(Cache<Object, Object> c, MagicKey hello) {
Address address = c.getCacheManager().getAddress();
ConsistentHash readConsistentHash = c.getAdvancedCache().getDistributionManager().getReadConsistentHash();
if (readConsistentHash.isKeyLocalToNode(address, hello)) {
InternalCacheEntry ice = c.getAdvancedCache().getDataContainer().get(hello);
assertNotNull("Entry not found on owner cache " + c, ice);
assertNotNull("Version is null on owner cache " + c, ice.getMetadata().version());
}
}
}