/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed 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 com.hazelcast.map.impl.querycache; import com.hazelcast.config.Config; import com.hazelcast.config.NearCacheConfig; import com.hazelcast.config.PredicateConfig; import com.hazelcast.config.QueryCacheConfig; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.IMap; import com.hazelcast.map.AbstractEntryEventTypesTest.Person; import com.hazelcast.map.QueryCache; import com.hazelcast.map.impl.event.MapEventPublisherImpl; import com.hazelcast.query.Predicate; import com.hazelcast.query.SqlPredicate; import com.hazelcast.test.HazelcastParametersRunnerFactory; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.Collection; import java.util.concurrent.Callable; import static com.hazelcast.map.impl.querycache.AbstractQueryCacheTestSupport.getMap; /** * Test basic QueryCache operation: create a map, put/update/remove values and assert size of query cache. * Parametrized with QueryCache option includeValues true/false & using default and query-cache-natural filtering strategies. */ @RunWith(Parameterized.class) @Parameterized.UseParametersRunnerFactory(HazelcastParametersRunnerFactory.class) @Category({QuickTest.class, ParallelTest.class}) public class QueryCacheBasicTest extends HazelcastTestSupport { private static final String TEST_MAP_NAME = "EntryListenerEventTypesTestMap"; private static final String QUERY_CACHE_NAME = "query-cache"; private Predicate predicate = new SqlPredicate("age > 50"); private HazelcastInstance instance; private IMap<Integer, Person> map; private QueryCache queryCache; @Parameterized.Parameter public boolean includeValues; @Parameterized.Parameter(1) public boolean useQueryCacheNaturalFilteringStrategy; @Parameterized.Parameter(2) public boolean useNearCache; @Parameterized.Parameters(name = "includeValues: {0}, useQueryCacheFilteringStrategy: {1}, nearCache: {2}") public static Collection<Object[]> parameters() { return Arrays.asList(new Object[][]{ {false, false, false}, {false, false, true}, {false, true, false}, {false, true, true}, {true, false, false}, {true, false, true}, {true, true, false}, {true, true, true} }); } // setup a map with 2 query caches, same predicate, one includes values, the other excludes values @Before public void setup() { Config config = new Config(); config.getMapConfig(TEST_MAP_NAME).addQueryCacheConfig( new QueryCacheConfig(QUERY_CACHE_NAME) .setPredicateConfig(new PredicateConfig(predicate)) .setIncludeValue(includeValues) ); if (useNearCache) { config.getMapConfig(TEST_MAP_NAME).setNearCacheConfig(new NearCacheConfig() .setCacheLocalEntries(true)); } config.setProperty(MapEventPublisherImpl.LISTENER_WITH_PREDICATE_PRODUCES_NATURAL_EVENT_TYPES.getName(), Boolean.toString(useQueryCacheNaturalFilteringStrategy)); instance = createHazelcastInstance(config); map = getMap(instance, "EntryListenerEventTypesTestMap"); queryCache = map.getQueryCache(QUERY_CACHE_NAME); } @After public void tearDown() { this.instance.shutdown(); } @Test public void entryAdded_whenValueMatchesPredicate() { map.put(1, new Person("a", 75)); assertEqualsEventually(new Callable<Integer>() { @Override public Integer call() throws Exception { return queryCache.size(); } }, 1); } @Test public void entryAdded_whenValueOutsidePredicate() { // when a value not matching predicate is put map.put(1, new Person("a", 25)); // then querycache does not contain any elements assertEqualsEventually(new Callable<Integer>() { @Override public Integer call() throws Exception { return queryCache.size(); } }, 0); } @Test public void entryRemoved_whenValueMatchesPredicate() { // when 2 values matching predicate are put & 1 is removed map.put(1, new Person("a", 75)); map.put(2, new Person("a", 95)); map.remove(1); // then size of querycache is 1 assertEqualsEventually(new Callable<Integer>() { @Override public Integer call() throws Exception { return queryCache.size(); } }, 1); } @Test public void entryRemoved_whenValueOutsidePredicate() { // when 2 values not matching predicate are put & 1 is removed map.put(1, new Person("a", 15)); map.put(2, new Person("a", 25)); map.remove(1); // then size of querycache is 0 assertEqualsEventually(new Callable<Integer>() { @Override public Integer call() throws Exception { return queryCache.size(); } }, 0); } @Test public void entryUpdated_whenOldValueOutside_newValueMatchesPredicate() { // when a value not matching predicate is put and is updated to match the predicate map.put(1, new Person("a", 15)); map.replace(1, new Person("a", 85)); // then size of querycache is 1 assertEqualsEventually(new Callable<Integer>() { @Override public Integer call() throws Exception { return queryCache.size(); } }, 1); } @Test public void entryUpdated_whenOldValueOutside_newValueOutsidePredicate() { // when a value not matching predicate is put and is updated to another value that does not match the predicate map.put(1, new Person("a", 15)); map.replace(1, new Person("a", 25)); // then size of querycache is 0 assertEqualsEventually(new Callable<Integer>() { @Override public Integer call() throws Exception { return queryCache.size(); } }, 0); } @Test public void entryUpdated_whenOldValueMatches_newValueMatchesPredicate() { // when a value matching predicate is put and is updated to another value that matches the predicate map.put(1, new Person("a", 55)); map.replace(1, new Person("a", 56)); // then size of querycache is 1 assertEqualsEventually(new Callable<Integer>() { @Override public Integer call() throws Exception { return queryCache.size(); } }, 1); } @Test public void entryUpdated_whenOldValueMatches_newValueOutsidePredicate() { // when a value matching predicate is put and is updated to another value that does not match the predicate map.put(1, new Person("a", 55)); map.replace(1, new Person("a", 15)); // then size of querycache is 0 assertEqualsEventually(new Callable<Integer>() { @Override public Integer call() throws Exception { return queryCache.size(); } }, 0); } @Test public void testKeySet_withFullKeyScan() { map.put(1, new Person("a", 55)); assertEqualsEventually(new Callable<Integer>() { @Override public Integer call() throws Exception { return queryCache.keySet().size(); } }, 1); } @Test public void testEntrySet_withFullKeyScan() { map.put(1, new Person("a", 55)); assertEqualsEventually(new Callable<Integer>() { @Override public Integer call() throws Exception { return queryCache.entrySet().size(); } }, 1); } }