/* * 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.Collection; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; import org.apache.ignite.IgniteLogger; import org.apache.ignite.cache.CacheWriteSynchronizationMode; import org.apache.ignite.cache.affinity.Affinity; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.events.CacheEvent; import org.apache.ignite.events.Event; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgnitePredicate; import org.apache.ignite.lang.IgniteRunnable; import org.apache.ignite.resources.IgniteInstanceResource; import org.apache.ignite.resources.LoggerResource; 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.TRANSACTIONAL; import static org.apache.ignite.cache.CacheMode.PARTITIONED; import static org.apache.ignite.cache.CacheRebalanceMode.SYNC; import static org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_PUT; import static org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_READ; import static org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_REMOVED; /** * Partitioned affinity test. */ @SuppressWarnings({"PointlessArithmeticExpression"}) public class GridCachePartitionedAffinitySelfTest extends GridCommonAbstractTest { /** Backup count. */ private static final int BACKUPS = 1; /** Grid count. */ private static final int GRIDS = 3; /** Fail flag. */ private static AtomicBoolean failFlag = new AtomicBoolean(false); /** */ private TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true); /** {@inheritDoc} */ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { CacheConfiguration cacheCfg = defaultCacheConfiguration(); cacheCfg.setCacheMode(PARTITIONED); cacheCfg.setBackups(BACKUPS); cacheCfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC); cacheCfg.setRebalanceMode(SYNC); cacheCfg.setAtomicityMode(TRANSACTIONAL); TcpDiscoverySpi spi = new TcpDiscoverySpi(); spi.setIpFinder(ipFinder); IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); cfg.setCacheConfiguration(cacheCfg); cfg.setDiscoverySpi(spi); return cfg; } /** {@inheritDoc} */ @Override protected void beforeTestsStarted() throws Exception { startGrids(GRIDS); } /** {@inheritDoc} */ @Override protected void afterTestsStopped() throws Exception { stopAllGrids(); } /** * @param ignite Grid. * @return Affinity. */ static Affinity<Object> affinity(Ignite ignite) { return ignite.affinity(DEFAULT_CACHE_NAME); } /** * @param aff Affinity. * @param key Key. * @return Nodes. */ private static Collection<? extends ClusterNode> nodes(Affinity<Object> aff, Object key) { return aff.mapKeyToPrimaryAndBackups(key); } /** @throws Exception If failed. */ public void testAffinity() throws Exception { waitTopologyUpdate(); Object key = 12345; Collection<? extends ClusterNode> nodes = null; for (int i = 0; i < GRIDS; i++) { Collection<? extends ClusterNode> affNodes = nodes(affinity(grid(i)), key); info("Affinity picture for grid [i=" + i + ", aff=" + U.toShortString(affNodes)); if (nodes == null) nodes = affNodes; else assert F.eqOrdered(nodes, affNodes); } } /** * @param g Grid. * @param keyCnt Key count. */ private static synchronized void printAffinity(Ignite g, int keyCnt) { X.println(">>>"); X.println(">>> Printing affinity for node: " + g.name()); Affinity<Object> aff = affinity(g); for (int i = 0; i < keyCnt; i++) { Collection<? extends ClusterNode> affNodes = nodes(aff, i); X.println(">>> Affinity nodes [key=" + i + ", partition=" + aff.partition(i) + ", nodes=" + U.nodes2names(affNodes) + ", ids=" + U.nodeIds(affNodes) + ']'); } partitionMap(g); } /** @param g Grid. */ private static void partitionMap(Ignite g) { X.println(">>> Full partition map for grid: " + g.name()); X.println(">>> " + dht(g.cache(DEFAULT_CACHE_NAME)).topology().partitionMap(false).toFullString()); } /** @throws Exception If failed. */ @SuppressWarnings("BusyWait") private void waitTopologyUpdate() throws Exception { GridTestUtils.waitTopologyUpdate(DEFAULT_CACHE_NAME, BACKUPS, log()); } /** @throws Exception If failed. */ public void testAffinityWithPut() throws Exception { waitTopologyUpdate(); Ignite mg = grid(0); IgniteCache<Integer, String> mc = mg.cache(DEFAULT_CACHE_NAME); int keyCnt = 10; printAffinity(mg, keyCnt); info("Registering event listener..."); // Register event listener on remote nodes. compute(mg.cluster().forRemotes()).run(new ListenerJob(keyCnt, mg.name())); for (int i = 0; i < keyCnt; i++) { if (failFlag.get()) fail("testAffinityWithPut failed."); info("Before putting key [key=" + i + ", igniteInstanceName=" + mg.name() + ']'); mc.put(i, Integer.toString(i)); if (failFlag.get()) fail("testAffinityWithPut failed."); } Thread.sleep(1000); if (failFlag.get()) fail("testAffinityWithPut failed."); } /** * */ private static class ListenerJob implements IgniteRunnable { /** Grid. */ @IgniteInstanceResource private Ignite ignite; /** Logger. */ @LoggerResource private IgniteLogger log; /** */ private int keyCnt; /** Master Ignite instance name. */ private String master; /** */ private AtomicInteger evtCnt = new AtomicInteger(); /** * */ private ListenerJob() { // No-op. } /** * @param keyCnt Key count. * @param master Master. */ private ListenerJob(int keyCnt, String master) { this.keyCnt = keyCnt; this.master = master; } /** {@inheritDoc} */ @Override public void run() { printAffinity(ignite, keyCnt); IgnitePredicate<Event> lsnr = new IgnitePredicate<Event>() { @Override public boolean apply(Event evt) { CacheEvent e = (CacheEvent)evt; switch (e.type()) { case EVT_CACHE_OBJECT_PUT: // new Exception("Dumping stack on grid [" + grid.name() + ", evtHash=" + // System.identityHashCode(evt) + ']').printStackTrace(System.out); log.info(">>> Grid cache event [igniteInstanceName=" + ignite.name() + ", name=" + e.name() + ", key=" + e.key() + ", oldVal=" + e.oldValue() + ", newVal=" + e.newValue() + ']'); evtCnt.incrementAndGet(); if (!ignite.name().equals(master) && evtCnt.get() > keyCnt * (BACKUPS + 1)) { failFlag.set(true); fail("Invalid put event count on grid [cnt=" + evtCnt.get() + ", igniteInstanceName=" + ignite.name() + ']'); } Collection<? extends ClusterNode> affNodes = nodes(affinity(ignite), e.key()); if (!affNodes.contains(ignite.cluster().localNode())) { failFlag.set(true); fail("Key should not be mapped to node [key=" + e.key() + ", node=" + ignite.name() + ']'); } break; default: failFlag.set(true); fail("Invalid cache event [igniteInstanceName=" + ignite + ", evt=" + evt + ']'); } return true; } }; ignite.events().localListen(lsnr, EVT_CACHE_OBJECT_PUT, EVT_CACHE_OBJECT_READ, EVT_CACHE_OBJECT_REMOVED); } } }