package org.infinispan.distribution; import static org.infinispan.test.TestingUtil.extractComponent; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; 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.commons.util.ObjectDuplicator; import org.infinispan.context.Flag; import org.infinispan.remoting.transport.Address; import org.infinispan.test.TestingUtil; import org.testng.annotations.Test; @Test(groups = {"functional", "smoke"}, testName = "distribution.DistSyncFuncTest") public class DistSyncFuncTest extends BaseDistFunctionalTest<Object, String> { public DistSyncFuncTest() { 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<>(); for (Cache<Object, String> c : caches) { boolean isOwner = isOwner(c, key); if (isOwner) owners.add(addressOf(c)); boolean secondCheck = getCacheTopology(c).getWriteOwners(key).contains(addressOf(c)); assertTrue(isOwner == secondCheck, "Second check failed for key " + key + " on cache " + addressOf(c) + " isO = " + isOwner + " sC = " + secondCheck); } // check consensus assertOwnershipConsensus(key); assertEquals(2, owners.size(),"Expected 2 owners for key " + key + " but was " + owners); } } private void assertOwnershipConsensus(String key) { List l1 = getCacheTopology(c1).getDistribution(key).writeOwners(); List l2 = getCacheTopology(c2).getDistribution(key).writeOwners(); List l3 = getCacheTopology(c3).getDistribution(key).writeOwners(); List l4 = getCacheTopology(c4).getDistribution(key).writeOwners(); assertEquals(l1, l2, "L1 "+l1+" and L2 "+l2+" don't agree."); assertEquals(l2, l3, "L2 "+l2+" and L3 "+l3+" don't agree."); assertEquals(l3, l4, "L3 "+l3+" and L4 "+l4+" don't agree."); } public void testBasicDistribution() throws Throwable { for (Cache<Object, String> c : caches) assertTrue(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(); log.trace("Here it begins"); Object retval = getFirstNonOwner("k1").putIfAbsent("k1", "value2"); if (testRetVals) assertEquals("value", retval); assertOnAllCachesAndOwnership("k1", "value"); c1.clear(); asyncWait(null, ClearCommand.class); retval = getFirstNonOwner("k1").putIfAbsent("k1", "value2"); eventually(() -> { try { assertOnAllCachesAndOwnership("k1", "value2"); } catch (AssertionError e) { log.debugf("Assertion failed once", e); return false; } return true; }); if (testRetVals) assertNull(retval); } public void testRemoveFromNonOwner() { initAndTest(); Object retval = getFirstNonOwner("k1").remove("k1"); asyncWait("k1", RemoveCommand.class, getSecondNonOwner("k1")); if (testRetVals) assertEquals("value", retval); assertRemovedOnAllCaches("k1"); } public void testConditionalRemoveFromNonOwner() { initAndTest(); log.trace("Here we start"); boolean retval = getFirstNonOwner("k1").remove("k1", "value2"); if (testRetVals) assertFalse(retval,"Should not have removed entry"); assertOnAllCachesAndOwnership("k1", "value"); assertEquals("value", caches.get(1).get("k1")); Cache<Object, String> owner = getFirstNonOwner("k1"); retval = owner.remove("k1", "value"); asyncWait("k1", RemoveCommand.class, getSecondNonOwner("k1")); if (testRetVals) assertTrue(retval, "Should have removed entry"); assertNull(caches.get(1).get("k1"), "expected null but received " + caches.get(1).get("k1")); assertRemovedOnAllCaches("k1"); } public void testReplaceFromNonOwner() { initAndTest(); Object retval = getFirstNonOwner("k1").replace("k1", "value2"); if (testRetVals) assertEquals("value", retval); asyncWait("k1", ReplaceCommand.class, getSecondNonOwner("k1")); assertOnAllCachesAndOwnership("k1", "value2"); c1.clear(); asyncWait(null, ClearCommand.class); retval = getFirstNonOwner("k1").replace("k1", "value2"); if (testRetVals) assertNull(retval); assertRemovedOnAllCaches("k1"); } public void testConditionalReplaceFromNonOwner() { initAndTest(); Cache<Object, String> nonOwner = getFirstNonOwner("k1"); boolean retval = nonOwner.replace("k1", "valueX", "value2"); if (testRetVals) assertFalse(retval, "Should not have replaced"); assertOnAllCachesAndOwnership("k1", "value"); assertFalse(extractComponent(nonOwner, DistributionManager.class).getCacheTopology().isWriteOwner("k1")); retval = nonOwner.replace("k1", "value", "value2"); asyncWait("k1", ReplaceCommand.class, getSecondNonOwner("k1")); if (testRetVals) assertTrue(retval,"Should have replaced"); assertOnAllCachesAndOwnership("k1", "value2"); } public void testClear() throws InterruptedException { for (Cache<Object, String> c : caches) assertTrue(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); assertOnAllCachesAndOwnership("k" + i, "value" + i); } // 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) assertFalse(c.isEmpty()); c1.clear(); asyncWait(null, ClearCommand.class); for (Cache<Object, String> c : caches) assertTrue(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.getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL).keySet(); for (Object key : keys) assertTrue(expKeys.remove(key)); assertTrue(expKeys.isEmpty(), "Did not see keys " + expKeys + " in iterator!"); Collection values = c.getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL).values(); for (Object value : values) assertTrue(expValues.remove(value)); assertTrue(expValues.isEmpty(), "Did not see keys " + expValues + " in iterator!"); Set<Map.Entry> entries = c.getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL).entrySet(); for (Map.Entry entry : entries) { assertTrue(expKeyEntries.remove(entry.getKey())); assertTrue(expValueEntries.remove(entry.getValue())); } assertTrue(expKeyEntries.isEmpty(), "Did not see keys " + expKeyEntries + " in iterator!"); assertTrue(expValueEntries.isEmpty(), "Did not see keys " + expValueEntries + " in iterator!"); } } }