/*
* JBoss, Home of Professional Open Source
* Copyright 2009 Red Hat Inc. and/or its affiliates and other
* contributors as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.distribution;
import org.infinispan.Cache;
import org.infinispan.commands.write.ClearCommand;
import org.infinispan.commands.write.PutKeyValueCommand;
import org.infinispan.commands.write.RemoveCommand;
import org.infinispan.commands.write.ReplaceCommand;
import org.infinispan.remoting.transport.Address;
import org.infinispan.test.TestingUtil;
import org.infinispan.util.ObjectDuplicator;
import org.testng.annotations.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@Test(groups = "functional", testName = "distribution.DistSyncFuncTest")
public class DistSyncFuncTest extends BaseDistFunctionalTest {
public DistSyncFuncTest() {
sync = true;
tx = false;
testRetVals = true;
}
public void testLocationConsensus() {
String[] keys = new String[100];
Random r = new Random();
for (int i = 0; i < 100; i++) keys[i] = Integer.toHexString(r.nextInt());
for (String key : keys) {
List<Address> owners = new ArrayList<Address>();
for (Cache<Object, String> c : caches) {
boolean isOwner = isOwner(c, key);
if (isOwner) owners.add(addressOf(c));
boolean secondCheck = getConsistentHash(c).locate(key, 2).contains(addressOf(c));
assert isOwner == secondCheck : "Second check failed for key " + key + " on cache " + addressOf(c) + " isO = " + isOwner + " sC = " + secondCheck;
}
// check consensus
assertOwnershipConsensus(key);
assert owners.size() == 2 : "Expected 2 owners for key " + key + " but was " + owners;
}
}
private void assertOwnershipConsensus(String key) {
List l1 = getConsistentHash(c1).locate(key, 2);
List l2 = getConsistentHash(c2).locate(key, 2);
List l3 = getConsistentHash(c3).locate(key, 2);
List l4 = getConsistentHash(c4).locate(key, 2);
assert l1.equals(l2) : "L1 "+l1+" and L2 "+l2+" don't agree.";
assert l2.equals(l3): "L2 "+l2+" and L3 "+l3+" don't agree.";
assert l3.equals(l4): "L3 "+l3+" and L4 "+l4+" don't agree.";
}
public void testBasicDistribution() throws Throwable {
for (Cache<Object, String> c : caches) assert c.isEmpty();
final Object k1 = getKeyForCache(caches.get(1));
getOwners(k1)[0].put(k1, "value");
// No non-owners have requested the key, so no invalidations
asyncWait(k1, PutKeyValueCommand.class);
for (Cache<Object, String> c : caches) {
if (isOwner(c, k1)) {
assertIsInContainerImmortal(c, k1);
} else {
assertIsNotInL1(c, k1);
}
}
// should be available everywhere!
assertOnAllCachesAndOwnership(k1, "value");
// and should now be in L1
for (Cache<Object, String> c : caches) {
if (isOwner(c, k1)) {
assertIsInContainerImmortal(c, k1);
} else {
assertIsInL1(c, k1);
}
}
}
public void testPutFromNonOwner() {
initAndTest();
Cache<Object, String> nonOwner = getFirstNonOwner("k1");
Object retval = nonOwner.put("k1", "value2");
asyncWait("k1", PutKeyValueCommand.class, getSecondNonOwner("k1"));
if (testRetVals) assert "value".equals(retval);
assertOnAllCachesAndOwnership("k1", "value2");
}
public void testPutIfAbsentFromNonOwner() {
initAndTest();
Object retval = getFirstNonOwner("k1").putIfAbsent("k1", "value2");
if (testRetVals) assert "value".equals(retval);
assertOnAllCachesAndOwnership("k1", "value");
c1.clear();
asyncWait(null, ClearCommand.class);
retval = getFirstNonOwner("k1").putIfAbsent("k1", "value2");
asyncWait("k1", PutKeyValueCommand.class, getSecondNonOwner("k1"));
if (testRetVals) assert null == retval;
assertOnAllCachesAndOwnership("k1", "value2");
}
public void testRemoveFromNonOwner() {
initAndTest();
Object retval = getFirstNonOwner("k1").remove("k1");
asyncWait("k1", RemoveCommand.class, getSecondNonOwner("k1"));
if (testRetVals) assert "value".equals(retval);
assertRemovedOnAllCaches("k1");
}
public void testConditionalRemoveFromNonOwner() {
initAndTest();
boolean retval = getFirstNonOwner("k1").remove("k1", "value2");
if (testRetVals) assert !retval : "Should not have removed entry";
assertOnAllCachesAndOwnership("k1", "value");
assert caches.get(1).get("k1").equals("value");
Cache<Object, String> owner = getFirstNonOwner("k1");
retval = owner.remove("k1", "value");
asyncWait("k1", RemoveCommand.class, getSecondNonOwner("k1"));
if (testRetVals) assert retval : "Should have removed entry";
assert caches.get(1).get("k1") == null : "expected null but received " + caches.get(1).get("k1");
assertRemovedOnAllCaches("k1");
}
public void testReplaceFromNonOwner() {
initAndTest();
Object retval = getFirstNonOwner("k1").replace("k1", "value2");
if (testRetVals) assert "value".equals(retval);
asyncWait("k1", ReplaceCommand.class, getSecondNonOwner("k1"));
assertOnAllCachesAndOwnership("k1", "value2");
c1.clear();
asyncWait(null, ClearCommand.class);
retval = getFirstNonOwner("k1").replace("k1", "value2");
if (testRetVals) assert retval == null;
assertRemovedOnAllCaches("k1");
}
public void testConditionalReplaceFromNonOwner() {
initAndTest();
Cache<Object, String> nonOwner = getFirstNonOwner("k1");
boolean retval = nonOwner.replace("k1", "valueX", "value2");
if (testRetVals) assert !retval : "Should not have replaced";
assertOnAllCachesAndOwnership("k1", "value");
assert !nonOwner.getAdvancedCache().getComponentRegistry().getComponent(DistributionManager.class).getLocality("k1").isLocal();
retval = nonOwner.replace("k1", "value", "value2");
asyncWait("k1", ReplaceCommand.class, getSecondNonOwner("k1"));
if (testRetVals) assert retval : "Should have replaced";
assertOnAllCachesAndOwnership("k1", "value2");
}
public void testClear() {
for (Cache<Object, String> c : caches) assert c.isEmpty();
for (int i = 0; i < 10; i++) {
getOwners("k" + i)[0].put("k" + i, "value" + i);
// There will be no caches to invalidate as this is the first command of the test
asyncWait("k" + i, PutKeyValueCommand.class);
}
// this will fill up L1 as well
for (int i = 0; i < 10; i++) assertOnAllCachesAndOwnership("k" + i, "value" + i);
for (Cache<Object, String> c : caches) assert !c.isEmpty();
c1.clear();
asyncWait(null, ClearCommand.class);
for (Cache<Object, String> c : caches) assert c.isEmpty();
}
public void testKeyValueEntryCollections() {
c1.put("1", "one");
asyncWait("1", PutKeyValueCommand.class);
c2.put("2", "two");
asyncWait("2", PutKeyValueCommand.class);
c3.put("3", "three");
asyncWait("3", PutKeyValueCommand.class);
c4.put("4", "four");
asyncWait("4", PutKeyValueCommand.class);
for (Cache c : caches) {
Set expKeys = TestingUtil.getInternalKeys(c);
Collection expValues = TestingUtil.getInternalValues(c);
Set expKeyEntries = ObjectDuplicator.duplicateSet(expKeys);
Collection expValueEntries = ObjectDuplicator.duplicateCollection(expValues);
Set keys = c.keySet();
for (Object key : keys) assert expKeys.remove(key);
assert expKeys.isEmpty() : "Did not see keys " + expKeys + " in iterator!";
Collection values = c.values();
for (Object value : values) assert expValues.remove(value);
assert expValues.isEmpty() : "Did not see keys " + expValues + " in iterator!";
Set<Map.Entry> entries = c.entrySet();
for (Map.Entry entry : entries) {
assert expKeyEntries.remove(entry.getKey());
assert expValueEntries.remove(entry.getValue());
}
assert expKeyEntries.isEmpty() : "Did not see keys " + expKeyEntries + " in iterator!";
assert expValueEntries.isEmpty() : "Did not see keys " + expValueEntries + " in iterator!";
}
}
}