/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ignite.internal.processors.cache.distributed.near; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; import org.apache.ignite.cache.CacheAtomicityMode; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.configuration.NearCacheConfiguration; import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.IgniteKernal; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.GridCacheContext; import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException; import org.apache.ignite.internal.processors.cache.KeyCacheObject; import org.apache.ignite.internal.processors.cache.distributed.GridCacheModuloAffinityFunction; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry; import org.apache.ignite.internal.util.lang.GridAbsPredicate; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.G; import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; import org.apache.ignite.testframework.GridTestUtils; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC; import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL; import static org.apache.ignite.cache.CacheMode.PARTITIONED; import static org.apache.ignite.cache.CacheRebalanceMode.NONE; import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; /** * Checks that readers are properly handled. */ public class GridCacheNearReadersSelfTest extends GridCommonAbstractTest { /** Number of grids. */ private int grids = 2; /** */ private TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true); /** Grid counter. */ private static AtomicInteger cntr = new AtomicInteger(0); /** Test cache affinity. */ private GridCacheModuloAffinityFunction aff = new GridCacheModuloAffinityFunction(); /** {@inheritDoc} */ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); CacheConfiguration cacheCfg = defaultCacheConfiguration(); cacheCfg.setCacheMode(PARTITIONED); cacheCfg.setWriteSynchronizationMode(FULL_SYNC); cacheCfg.setRebalanceMode(NONE); cacheCfg.setAffinity(aff); cacheCfg.setAtomicityMode(atomicityMode()); cacheCfg.setBackups(aff.backups()); NearCacheConfiguration nearCfg = new NearCacheConfiguration(); cacheCfg.setNearConfiguration(nearCfg); cfg.setCacheConfiguration(cacheCfg); TcpDiscoverySpi disco = new TcpDiscoverySpi(); disco.setIpFinder(ipFinder); cfg.setDiscoverySpi(disco); cfg.setUserAttributes(F.asMap(GridCacheModuloAffinityFunction.IDX_ATTR, cntr.getAndIncrement())); return cfg; } /** * @return Atomicity mode. */ protected CacheAtomicityMode atomicityMode() { return TRANSACTIONAL; } /** @throws Exception If failed. */ private void startGrids() throws Exception { assert grids > 0; assert aff.backups() >= 0; startGrids(grids); awaitPartitionMapExchange(); } /** {@inheritDoc} */ @Override protected void afterTest() throws Exception { stopAllGrids(); grids = -1; aff.reset(); cntr.set(0); } /** * @param nodeId Node ID. * @return Grid. */ private Ignite grid(UUID nodeId) { return G.ignite(nodeId); } /** @throws Exception If failed. */ public void testTwoNodesTwoKeysNoBackups() throws Exception { aff.backups(0); grids = 2; aff.partitions(grids); startGrids(); ClusterNode n1 = F.first(aff.nodes(aff.partition(1), grid(0).cluster().nodes())); final ClusterNode n2 = F.first(aff.nodes(aff.partition(2), grid(0).cluster().nodes())); assertNotNull(n1); assertNotNull(n2); assertNotSame(n1, n2); assertFalse("Nodes cannot be equal: " + n1, n1.equals(n2)); Ignite g1 = grid(n1.id()); Ignite g2 = grid(n2.id()); IgniteCache<Integer, String> cache1 = g1.cache(DEFAULT_CACHE_NAME); IgniteCache<Integer, String> cache2 = g2.cache(DEFAULT_CACHE_NAME); // Store some values in cache. assertNull(cache1.getAndPut(1, "v1")); assertNull(cache1.getAndPut(2, "v2")); GridDhtCacheEntry e1 = (GridDhtCacheEntry)dht(cache1).entryEx(1); GridDhtCacheEntry e2 = (GridDhtCacheEntry)dht(cache2).entryEx(2); assertNotNull(e1.readers()); assertTrue(cache1.containsKey(1)); assertTrue(cache1.containsKey(2)); assertNotNull(nearPeek(cache1, 1)); assertNotNull(nearPeek(cache1, 2)); assertNotNull(dhtPeek(cache1, 1)); assertNull(dhtPeek(cache1, 2)); assertNull(nearPeek(cache2, 1)); assertNotNull(dhtPeek(cache2, 2)); // Node2 should have node1 in reader's map, since request to // put key 2 came from node1. assertTrue(e2.readers().contains(n1.id())); e1 = (GridDhtCacheEntry)dht(cache1).entryEx(1); // Node1 should not have node2 in readers map yet. assertFalse(e1.readers().contains(n2.id())); // Get key1 on node2. assertEquals("v1", cache2.get(1)); // Check that key1 is in near cache of cache2. assertNotNull(nearPeek(cache2, 1)); e1 = (GridDhtCacheEntry)dht(cache1).entryEx(1); // Now node1 should have node2 in readers map. assertTrue(e1.readers().contains(n2.id())); // Evict locally from cache2. cache2.localEvict(Collections.singleton(1)); assertNull(nearPeek(cache2, 1)); assertNull(dhtPeek(cache2, 1)); // Node 1 still has node2 in readers map. assertTrue(e1.readers().contains(n2.id())); assertNotNull(cache1.getAndPut(1, "z1")); final GridDhtCacheEntry e1f = e1; GridTestUtils.waitForCondition(new GridAbsPredicate() { @Override public boolean apply() { try { return !e1f.readers().contains(n2.id()); } catch (GridCacheEntryRemovedException ignored) { return true; } catch (Exception e) { throw new RuntimeException(e); } } }, 5000); // Node 1 still has node2 in readers map. assertFalse(((GridDhtCacheEntry)dht(cache1).entryEx(1)).readers().contains(n2.id())); } /** @throws Exception If failed. */ public void testTwoNodesTwoKeysOneBackup() throws Exception { aff.backups(1); grids = 2; aff.partitions(grids); startGrids(); ClusterNode n1 = F.first(aff.nodes(aff.partition(1), grid(0).cluster().nodes())); ClusterNode n2 = F.first(aff.nodes(aff.partition(2), grid(0).cluster().nodes())); assertNotNull(n1); assertNotNull(n2); assertNotSame(n1, n2); assertFalse("Nodes cannot be equal: " + n1, n1.equals(n2)); Ignite g1 = grid(n1.id()); Ignite g2 = grid(n2.id()); awaitPartitionMapExchange(); GridCacheContext ctx = ((IgniteKernal) g1).internalCache(DEFAULT_CACHE_NAME).context(); List<KeyCacheObject> cacheKeys = F.asList(ctx.toCacheKeyObject(1), ctx.toCacheKeyObject(2)); IgniteInternalFuture<Object> f1 = ((IgniteKernal)g1).internalCache(DEFAULT_CACHE_NAME).preloader().request( cacheKeys, new AffinityTopologyVersion(2)); if (f1 != null) f1.get(); IgniteInternalFuture<Object> f2 = ((IgniteKernal)g2).internalCache(DEFAULT_CACHE_NAME).preloader().request( cacheKeys, new AffinityTopologyVersion(2)); if (f2 != null) f2.get(); IgniteCache<Integer, String> cache1 = g1.cache(DEFAULT_CACHE_NAME); IgniteCache<Integer, String> cache2 = g2.cache(DEFAULT_CACHE_NAME); assertEquals(g1.affinity(DEFAULT_CACHE_NAME).mapKeyToNode(1), g1.cluster().localNode()); assertFalse(g1.affinity(DEFAULT_CACHE_NAME).mapKeyToNode(2).equals(g1.cluster().localNode())); assertEquals(g1.affinity(DEFAULT_CACHE_NAME).mapKeyToNode(2), g2.cluster().localNode()); assertFalse(g2.affinity(DEFAULT_CACHE_NAME).mapKeyToNode(1).equals(g2.cluster().localNode())); // Store first value in cache. assertNull(cache1.getAndPut(1, "v1")); assertTrue(cache1.containsKey(1)); assertTrue(cache2.containsKey(1)); assertEquals("v1", nearPeek(cache1, 1)); assertEquals("v1", nearPeek(cache2, 1)); assertEquals("v1", dhtPeek(cache1, 1)); assertEquals("v1", dhtPeek(cache2, 1)); assertNull(near(cache1).peekEx(1)); assertNull(near(cache2).peekEx(1)); GridDhtCacheEntry e1 = (GridDhtCacheEntry)dht(cache1).entryEx(1); // Store second value in cache. assertNull(cache1.getAndPut(2, "v2")); assertTrue(cache1.containsKey(2)); assertTrue(cache2.containsKey(2)); assertEquals("v2", nearPeek(cache1, 2)); assertEquals("v2", nearPeek(cache2, 2)); assertEquals("v2", dhtPeek(cache1, 2)); assertEquals("v2", dhtPeek(cache2, 2)); assertNull(near(cache1).peekEx(2)); assertNull(near(cache2).peekEx(2)); GridDhtCacheEntry c2e2 = (GridDhtCacheEntry)dht(cache2).entryEx(2); // Nodes are backups of each other, so no readers should be added. assertFalse(c2e2.readers().contains(n1.id())); assertFalse(e1.readers().contains(n2.id())); // Get key1 on node2 (value should come from local DHT cache, as it has a backup). assertEquals("v1", cache2.get(1)); // Since DHT cache2 has the value, Near cache2 should not have it. assertNull(near(cache2).peekEx(1)); e1 = (GridDhtCacheEntry)dht(cache1).entryEx(1); // Since v1 was retrieved locally from cache2, cache1 should not know about it. assertFalse(e1.readers().contains(n2.id())); // Evict locally from cache2. // It should not be successful since it's not allowed to evict entry on backup node. cache2.localEvict(Collections.singleton(1)); assertNull(near(cache2).peekEx(1)); assertEquals("v1", dhtPeek(cache2, 1)); assertEquals("v1", cache1.getAndPut(1, "z1")); e1 = (GridDhtCacheEntry)dht(cache1).entryEx(1); // Node 1 should not have node2 in readers map. assertFalse(e1.readers().contains(n2.id())); assertNull(near(cache2).peekEx(1)); assertEquals("z1", dhtPeek(cache2, 1)); } /** @throws Exception If failed. */ public void testPutAllManyKeysOneReader() throws Exception { aff.backups(1); grids = 4; aff.partitions(grids); startGrids(); try { IgniteCache<Object, Object> prj0 = grid(0).cache(DEFAULT_CACHE_NAME); IgniteCache<Object, Object> prj1 = grid(1).cache(DEFAULT_CACHE_NAME); Map<Integer, Integer> putMap = new HashMap<>(); int size = 100; for (int i = 0; i < size; i++) putMap.put(i, i); prj0.putAll(putMap); for (int i = 0; i < size; i++) putMap.put(i, i * i); prj1.putAll(putMap); for (int i = 0; i < size; i++) { assertEquals(i * i, prj0.get(i)); assertEquals(i * i, prj1.get(i)); } } finally { stopAllGrids(); } } /** @throws Exception If failed. */ public void testPutAllManyKeysTwoReaders() throws Exception { aff.backups(1); grids = 5; aff.partitions(grids); startGrids(); try { IgniteCache<Object, Object> prj0 = grid(0).cache(DEFAULT_CACHE_NAME); IgniteCache<Object, Object> prj1 = grid(1).cache(DEFAULT_CACHE_NAME); IgniteCache<Object, Object> prj2 = grid(2).cache(DEFAULT_CACHE_NAME); Map<Integer, Integer> putMap = new HashMap<>(); int size = 100; for (int i = 0; i < size; i++) putMap.put(i, i); prj0.putAll(putMap); for (int i = 0; i < size; i++) putMap.put(i, i * i); prj1.putAll(putMap); for (int i = 0; i < size; i++) putMap.put(i, i * i * i); prj2.putAll(putMap); for (int i = 0; i < size; i++) { assertEquals(i * i * i, prj0.get(i)); assertEquals(i * i * i, prj1.get(i)); assertEquals(i * i * i, prj2.get(i)); } } finally { stopAllGrids(); } } /** @throws Exception If failed. */ public void testBackupEntryReaders() throws Exception { aff.backups(1); grids = 2; aff.partitions(grids); startGrids(); Collection<ClusterNode> nodes = new ArrayList<>(aff.nodes(aff.partition(1), grid(0).cluster().nodes())); ClusterNode primary = F.first(nodes); assert primary != null; nodes.remove(primary); ClusterNode backup = F.first(nodes); assert backup != null; assertNotSame(primary, backup); assertFalse("Nodes cannot be equal: " + primary, primary.equals(backup)); IgniteCache<Integer, String> cache1 = grid(primary.id()).cache(DEFAULT_CACHE_NAME); IgniteCache<Integer, String> cache2 = grid(backup.id()).cache(DEFAULT_CACHE_NAME); // Store a values in cache. assertNull(cache1.getAndPut(1, "v1")); GridDhtCacheEntry e1 = (GridDhtCacheEntry)dht(cache1).peekEx(1); GridDhtCacheEntry e2 = (GridDhtCacheEntry)dht(cache2).peekEx(1); assertNull(e1); assertNull(e2); } /** @throws Exception If failed. */ @SuppressWarnings({"SizeReplaceableByIsEmpty"}) public void testImplicitLockReaders() throws Exception { grids = 3; aff.reset(grids, 1); startGrids(); int key1 = 3; String val1 = Integer.toString(key1); assertEquals(grid(0).localNode(), F.first(aff.nodes(aff.partition(key1), grid(0).cluster().nodes()))); int key2 = 1; String val2 = Integer.toString(key2); assertEquals(grid(1).localNode(), F.first(aff.nodes(aff.partition(key2), grid(1).cluster().nodes()))); IgniteCache<Integer, String> cache = jcache(0); assertNull(cache.getAndPut(key1, val1)); assertEquals(val1, dhtPeek(0, key1)); assertEquals(val1, dhtPeek(1, key1)); assertNull(dhtPeek(2, key1)); assertNull(near(0).peekEx(key1)); assertNull(near(1).peekEx(key1)); assertNull(near(2).peekEx(key1)); cache.put(key2, val2); assertNull(dhtPeek(0, key2)); assertEquals(val2, dhtPeek(1, key2)); assertEquals(val2, dhtPeek(2, key2)); assertEquals(val2, near(0).peekEx(key2).wrap().getValue()); assertNull(near(1).peekEx(key2)); assertNull(near(2).peekEx(key2)); String val22 = val2 + "2"; cache.put(key2, val22); assertNull(dhtPeek(0, key2)); assertEquals(val22, dhtPeek(1, key2)); assertEquals(val22, dhtPeek(2, key2)); assertEquals(val22, near(0).peekEx(key2).wrap().getValue()); assertNull(near(1).peekEx(key2)); assertNull(near(2).peekEx(key2)); cache.remove(key2); assertNull(dhtPeek(0, key2)); assertNull(dhtPeek(1, key2)); assertNull(dhtPeek(2, key2)); assertTrue(near(0).peekEx(key2) == null || near(0).peekEx(key2).deleted()); assertNull(near(1).peekEx(key2)); assertNull(near(2).peekEx(key2)); cache.remove(key1); assertNull(dhtPeek(0, key1)); assertNull(dhtPeek(1, key1)); assertNull(dhtPeek(2, key1)); assertNull(near(0).peekEx(key1)); assertNull(near(1).peekEx(key1)); assertNull(near(2).peekEx(key1)); for (int i = 0; i < grids; i++) { assert !jcache(i).isLocalLocked(key1, false); assert !jcache(i).isLocalLocked(key2, false); assert jcache(i).localSize() == 0; } } /** @throws Exception If failed. */ public void testExplicitLockReaders() throws Exception { if (atomicityMode() == ATOMIC) return; grids = 3; aff.reset(grids, 1); startGrids(); int key1 = 3; String val1 = Integer.toString(key1); assertEquals(grid(0).localNode(), F.first(aff.nodes(aff.partition(key1), grid(0).cluster().nodes()))); int key2 = 1; String val2 = Integer.toString(key2); assertEquals(grid(1).localNode(), F.first(aff.nodes(aff.partition(key2), grid(1).cluster().nodes()))); IgniteCache<Integer, String> cache = jcache(0); Lock lock1 = cache.lock(key1); lock1.lock(); try { // Nested lock. Lock lock2 = cache.lock(key2); lock2.lock(); try { assertNull(cache.getAndPut(key1, val1)); assertEquals(val1, dhtPeek(0, key1)); assertEquals(val1, dhtPeek(1, key1)); assertNull(dhtPeek(2, key1)); // Since near entry holds the lock, it should // contain correct value. assertEquals(val1, near(0).peekEx(key1).wrap().getValue()); assertNull(near(1).peekEx(key1)); assertNull(near(2).peekEx(key1)); cache.put(key2, val2); assertNull(dhtPeek(0, key2)); assertEquals(val2, dhtPeek(1, key2)); assertEquals(val2, dhtPeek(2, key2)); assertEquals(val2, near(0).peekEx(key2).wrap().getValue()); assertNull(near(1).peekEx(key2)); assertNull(near(2).peekEx(key2)); String val22 = val2 + "2"; cache.put(key2, val22); assertNull(dhtPeek(0, key2)); assertEquals(val22, dhtPeek(1, key2)); assertEquals(val22, dhtPeek(2, key2)); assertEquals(val22, near(0).peekEx(key2).wrap().getValue()); assertNull(near(1).peekEx(key2)); assertNull(near(2).peekEx(key2)); cache.remove(key2); assertNull(dhtPeek(0, key2)); assertNull(dhtPeek(1, key2)); assertNull(dhtPeek(2, key2)); assertNull(dht(0).peekEx(key2)); assertNotNull(dht(1).peekEx(key2)); assertNotNull(dht(2).peekEx(key2)); assertNotNull(near(0).peekEx(key2)); assertNull(near(1).peekEx(key2)); assertNull(near(2).peekEx(key2)); } finally { lock2.unlock(); } } finally { lock1.unlock(); } } }