/* * 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; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import javax.cache.Cache; import org.apache.ignite.IgniteCache; import org.apache.ignite.cache.affinity.Affinity; import org.apache.ignite.cache.query.QueryCursor; import org.apache.ignite.cache.query.ScanQuery; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.events.CacheQueryExecutedEvent; import org.apache.ignite.events.CacheQueryReadEvent; import org.apache.ignite.events.Event; import org.apache.ignite.lang.IgniteBiPredicate; import org.apache.ignite.lang.IgnitePredicate; import org.apache.ignite.testframework.junits.IgniteCacheConfigVariationsAbstractTest; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.apache.ignite.cache.CacheMode.PARTITIONED; import static org.apache.ignite.cache.CacheMode.REPLICATED; import static org.apache.ignite.events.EventType.EVT_CACHE_QUERY_EXECUTED; import static org.apache.ignite.events.EventType.EVT_CACHE_QUERY_OBJECT_READ; import static org.apache.ignite.internal.processors.cache.query.CacheQueryType.SCAN; /** * Config Variations query tests. */ public class IgniteCacheConfigVariationsQueryTest extends IgniteCacheConfigVariationsAbstractTest { /** */ public static final int CNT = 50; /** */ private Map<Object, Object> evtMap; /** */ private CountDownLatch readEvtLatch; /** */ private CountDownLatch execEvtLatch; /** */ private IgnitePredicate[] objReadLsnrs; /** */ private IgnitePredicate[] qryExecLsnrs; /** */ private Map<Object, Object> expMap; /** * @throws Exception If failed. */ @SuppressWarnings("serial") public void testScanQuery() throws Exception { runInAllDataModes(new TestRunnable() { @Override public void run() throws Exception { try { IgniteCache<Object, Object> cache = jcache(); Map<Object, Object> map = new HashMap<Object, Object>() {{ for (int i = 0; i < CNT; i++) put(key(i), value(i)); }}; registerEventListeners(map); for (Map.Entry<Object, Object> e : map.entrySet()) cache.put(e.getKey(), e.getValue()); // Scan query. QueryCursor<Cache.Entry<Object, Object>> qry = cache.query(new ScanQuery()); checkQueryResults(map, qry); } finally { stopListeners(); } } }); } /** * @throws Exception If failed. */ public void testScanPartitionQuery() throws Exception { runInAllDataModes(new TestRunnable() { @Override public void run() throws Exception { IgniteCache<Object, Object> cache = jcache(); GridCacheContext cctx = ((IgniteCacheProxy)cache).context(); Map<Integer, Map<Object, Object>> entries = new HashMap<>(); for (int i = 0; i < CNT; i++) { Object key = key(i); Object val = value(i); cache.put(key, val); int part = cctx.affinity().partition(key); Map<Object, Object> partEntries = entries.get(part); if (partEntries == null) entries.put(part, partEntries = new HashMap<>()); partEntries.put(key, val); } for (int i = 0; i < cctx.affinity().partitions(); i++) { try { Map<Object, Object> exp = entries.get(i); if (exp == null) System.out.println(); registerEventListeners(exp); ScanQuery<Object, Object> scan = new ScanQuery<>(i); Collection<Cache.Entry<Object, Object>> actual = cache.query(scan).getAll(); assertEquals("Failed for partition: " + i, exp == null ? 0 : exp.size(), actual.size()); if (exp != null) { for (Cache.Entry<Object, Object> entry : actual) assertTrue(entry.getValue().equals(exp.get(entry.getKey()))); } checkEvents(); } finally { stopListeners(); } } } }); } /** * @throws Exception If failed. */ @SuppressWarnings("SubtractionInCompareTo") public void testScanFilters() throws Exception { runInAllDataModes(new TestRunnable() { @Override public void run() throws Exception { try { IgniteCache<Object, Object> cache = jcache(); IgniteBiPredicate<Object, Object> p = new IgniteBiPredicate<Object, Object>() { @Override public boolean apply(Object k, Object v) { assertNotNull(k); assertNotNull(v); return valueOf(k) >= 20 && valueOf(v) < 40; } }; Map<Object, Object> exp = new HashMap<>(); for (int i = 0; i < CNT; i++) { Object key = key(i); Object val = value(i); cache.put(key, val); if (p.apply(key, val)) exp.put(key, val); } registerEventListeners(exp, true); QueryCursor<Cache.Entry<Object, Object>> q = cache.query(new ScanQuery<>(p)); checkQueryResults(exp, q); } finally { stopListeners(); } } }); } /** * @throws Exception If failed. */ @SuppressWarnings("SubtractionInCompareTo") public void testLocalScanQuery() throws Exception { runInAllDataModes(new TestRunnable() { @Override public void run() throws Exception { try { IgniteCache<Object, Object> cache = jcache(); ClusterNode locNode = testedGrid().cluster().localNode(); Affinity<Object> affinity = testedGrid().affinity(cacheName()); Map<Object, Object> map = new HashMap<>(); for (int i = 0; i < CNT; i++) { Object key = key(i); Object val = value(i); cache.put(key, val); if (!isClientMode() && (cacheMode() == REPLICATED || affinity.isPrimary(locNode, key))) map.put(key, val); } registerEventListeners(map); QueryCursor<Cache.Entry<Object, Object>> q = cache.query(new ScanQuery<>().setLocal(true)); checkQueryResults(map, q); } finally { stopListeners(); } } }); } /** * @throws Exception If failed. */ @SuppressWarnings("SubtractionInCompareTo") public void testScanQueryLocalFilter() throws Exception { runInAllDataModes(new TestRunnable() { @Override public void run() throws Exception { try { IgniteCache<Object, Object> cache = jcache(); ClusterNode locNode = testedGrid().cluster().localNode(); Map<Object, Object> map = new HashMap<>(); IgniteBiPredicate<Object, Object> filter = new IgniteBiPredicate<Object, Object>() { @Override public boolean apply(Object k, Object v) { assertNotNull(k); assertNotNull(v); return valueOf(k) >= 20 && valueOf(v) < 40; } }; for (int i = 0; i < CNT; i++) { Object key = key(i); Object val = value(i); cache.put(key, val); if (!isClientMode() && (cacheMode() == REPLICATED || testedGrid().affinity(cacheName()).isPrimary(locNode, key)) && filter.apply(key, val)) map.put(key, val); } registerEventListeners(map, true); QueryCursor<Cache.Entry<Object, Object>> q = cache.query(new ScanQuery<>(filter).setLocal(true)); checkQueryResults(map, q); } finally { stopListeners(); } } }); } /** * @throws Exception If failed. */ @SuppressWarnings("SubtractionInCompareTo") public void testScanQueryPartitionFilter() throws Exception { runInAllDataModes(new TestRunnable() { @Override public void run() throws Exception { IgniteCache<Object, Object> cache = jcache(); Affinity<Object> affinity = testedGrid().affinity(cacheName()); Map<Integer, Map<Object, Object>> partMap = new HashMap<>(); IgniteBiPredicate<Object, Object> filter = new IgniteBiPredicate<Object, Object>() { @Override public boolean apply(Object k, Object v) { assertNotNull(k); assertNotNull(v); return valueOf(k) >= 20 && valueOf(v) < 40; } }; for (int i = 0; i < CNT; i++) { Object key = key(i); Object val = value(i); cache.put(key, val); if (filter.apply(key, val)) { int part = affinity.partition(key); Map<Object, Object> map = partMap.get(part); if (map == null) partMap.put(part, map = new HashMap<>()); map.put(key, val); } } for (int part = 0; part < affinity.partitions(); part++) { try { Map<Object, Object> expMap = partMap.get(part); expMap = expMap == null ? Collections.emptyMap() : expMap; registerEventListeners(expMap, true); QueryCursor<Cache.Entry<Object, Object>> q = cache.query(new ScanQuery<>(part, filter)); checkQueryResults(expMap, q); } finally { stopListeners(); } } } }); } /** * @param expMap Expected map. * @param cursor Query cursor. */ private void checkQueryResults(Map<Object, Object> expMap, QueryCursor<Cache.Entry<Object, Object>> cursor) throws InterruptedException { Iterator<Cache.Entry<Object, Object>> iter = cursor.iterator(); try { assertNotNull(iter); int cnt = 0; while (iter.hasNext()) { Cache.Entry<Object, Object> e = iter.next(); assertNotNull(e.getKey()); assertNotNull(e.getValue()); Object expVal = expMap.get(e.getKey()); assertNotNull("Failed to resolve expected value for key: " + e.getKey(), expVal); assertEquals(expVal, e.getValue()); cnt++; } assertEquals(expMap.size(), cnt); } finally { cursor.close(); } checkEvents(); } /** * Registers event listeners. * @param expMap Expected read events count. */ private void registerEventListeners(Map<Object, Object> expMap) { registerEventListeners(expMap, false); } /** * Registers event listeners. * @param expMap Expected read events count. * @param filterExp Scan query uses filter. */ private void registerEventListeners(Map<Object, Object> expMap, final boolean filterExp) { this.expMap = expMap != null ? expMap : Collections.emptyMap(); Set<ClusterNode> affNodes= new HashSet<>(); if (cacheMode() != REPLICATED) { Affinity<Object> aff = testedGrid().affinity(cacheName()); for (Object key : this.expMap.keySet()) affNodes.add(aff.mapKeyToNode(key)); } int execEvtCnt = cacheMode() == REPLICATED || (cacheMode() == PARTITIONED && affNodes.isEmpty()) ? 1 : affNodes.size(); evtMap = new ConcurrentHashMap<>(); readEvtLatch = new CountDownLatch(this.expMap.size()); execEvtLatch = new CountDownLatch(execEvtCnt); objReadLsnrs = new IgnitePredicate[gridCount()]; qryExecLsnrs = new IgnitePredicate[gridCount()]; for (int i = 0; i < gridCount(); i++) { IgnitePredicate<Event> pred = new IgnitePredicate<Event>() { @Override public boolean apply(Event evt) { assertTrue("Event: " + evt, evt instanceof CacheQueryReadEvent); CacheQueryReadEvent<Object, Object> qe = (CacheQueryReadEvent<Object, Object>)evt; assertEquals(SCAN.name(), qe.queryType()); assertEquals(cacheName(), qe.cacheName()); assertNull(qe.className()); assertNull(qe.clause()); assertEquals(filterExp, qe.scanQueryFilter() != null); assertNull(qe.continuousQueryFilter()); assertNull(qe.arguments()); evtMap.put(qe.key(), qe.value()); assertFalse(readEvtLatch.getCount() == 0); readEvtLatch.countDown(); return true; } }; grid(i).events().localListen(pred, EVT_CACHE_QUERY_OBJECT_READ); objReadLsnrs[i] = pred; IgnitePredicate<Event> execPred = new IgnitePredicate<Event>() { @Override public boolean apply(Event evt) { assertTrue("Event: " + evt, evt instanceof CacheQueryExecutedEvent); CacheQueryExecutedEvent qe = (CacheQueryExecutedEvent)evt; assertEquals(SCAN.name(), qe.queryType()); assertEquals(cacheName(), qe.cacheName()); assertNull(qe.className()); assertNull(qe.clause()); assertEquals(filterExp, qe.scanQueryFilter() != null); assertNull(qe.continuousQueryFilter()); assertNull(qe.arguments()); assertFalse("Too many events.", execEvtLatch.getCount() == 0); execEvtLatch.countDown(); return true; } }; grid(i).events().localListen(execPred, EVT_CACHE_QUERY_EXECUTED); qryExecLsnrs[i] = execPred; } } /** * Stops listenening. */ private void stopListeners() { for (int i = 0; i < gridCount(); i++) { grid(i).events().stopLocalListen(objReadLsnrs[i]); grid(i).events().stopLocalListen(qryExecLsnrs[i]); } } /** * @throws InterruptedException If failed. */ private void checkEvents() throws InterruptedException { assertTrue(execEvtLatch.await(1000, MILLISECONDS)); assertTrue(readEvtLatch.await(1000, MILLISECONDS)); assertEquals(expMap.size(), evtMap.size()); for (Map.Entry<Object, Object> e : expMap.entrySet()) assertEquals(e.getValue(), evtMap.get(e.getKey())); } }