/* * 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.cache; import com.hazelcast.cache.impl.CacheProxy; import com.hazelcast.cache.impl.HazelcastServerCachingProvider; import com.hazelcast.config.CacheConfig; import com.hazelcast.config.Config; import com.hazelcast.config.EvictionConfig; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.spi.properties.GroupProperty; import com.hazelcast.test.HazelcastParametersRunnerFactory; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.TestHazelcastInstanceFactory; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import javax.cache.Cache; import javax.cache.CacheManager; import javax.cache.spi.CachingProvider; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; @Ignore @RunWith(Parameterized.class) @Parameterized.UseParametersRunnerFactory(HazelcastParametersRunnerFactory.class) @Category({QuickTest.class, ParallelTest.class}) public class CachePartitionIteratorMigrationTest extends HazelcastTestSupport { @Parameterized.Parameter public boolean prefetchValues; @Parameterized.Parameters(name = "prefetchValues:{0}") public static Iterable<Object[]> parameters() { return Arrays.asList(new Object[]{Boolean.TRUE}, new Object[]{Boolean.FALSE}); } protected CachingProvider createCachingProvider(HazelcastInstance server) { return HazelcastServerCachingProvider.createCachingProvider(server); } private <K, V> CacheProxy<K, V> getCacheProxy(CachingProvider cachingProvider) { String cacheName = randomString(); CacheManager cacheManager = cachingProvider.getCacheManager(); CacheConfig<K, V> config = new CacheConfig<K, V>(); config.getEvictionConfig().setMaximumSizePolicy(EvictionConfig.MaxSizePolicy.ENTRY_COUNT).setSize(10000000); return (CacheProxy<K, V>) cacheManager.createCache(cacheName, config); } @Test public void test_DoesNotReturn_DuplicateEntry_When_Rehashing_Happens() throws Exception { HazelcastInstance instance = createHazelcastInstance(); CacheProxy<String, String> proxy = getCacheProxy(createCachingProvider(instance)); HashSet<String> readKeys = new HashSet<String>(); String value = "initialValue"; putValuesToPartition(instance, proxy, value, 1, 100); Iterator<Cache.Entry<String, String>> iterator = proxy.iterator(10, 1, prefetchValues); assertUniques(readKeys, iterator, 50); // force rehashing putValuesToPartition(instance, proxy, randomString(), 1, 150); assertUniques(readKeys, iterator); } @Test public void test_DoesNotReturn_DuplicateEntry_When_Migration_Happens() throws Exception { Config config = getConfig(); config.setProperty(GroupProperty.PARTITION_COUNT.getName(), "2"); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(); HazelcastInstance instance = factory.newHazelcastInstance(config); CacheProxy<String, String> proxy = getCacheProxy(createCachingProvider(instance)); HashSet<String> readKeysP1 = new HashSet<String>(); HashSet<String> readKeysP2 = new HashSet<String>(); String value = "value"; putValuesToPartition(instance, proxy, value, 0, 100); putValuesToPartition(instance, proxy, value, 1, 100); Iterator<Cache.Entry<String, String>> iteratorP1 = proxy.iterator(10, 0, prefetchValues); Iterator<Cache.Entry<String, String>> iteratorP2 = proxy.iterator(10, 1, prefetchValues); assertUniques(readKeysP1, iteratorP1, 50); assertUniques(readKeysP2, iteratorP2, 50); // force migration factory.newHazelcastInstance(config); // force rehashing putValuesToPartition(instance, proxy, randomString(), 0, 150); putValuesToPartition(instance, proxy, randomString(), 1, 150); assertUniques(readKeysP1, iteratorP1); assertUniques(readKeysP2, iteratorP2); } private void assertUniques(HashSet<String> readKeys, Iterator<Cache.Entry<String, String>> iterator) { while (iterator.hasNext()) { Cache.Entry<String, String> entry = iterator.next(); boolean unique = readKeys.add(entry.getKey()); Assert.assertTrue(unique); } } private void assertUniques(HashSet<String> readKeys, Iterator<Cache.Entry<String, String>> iterator, int numberOfItemsToRead) { int count = 0; while (iterator.hasNext() && count++ < numberOfItemsToRead) { Cache.Entry<String, String> entry = iterator.next(); boolean unique = readKeys.add(entry.getKey()); Assert.assertTrue(unique); } } private void putValuesToPartition(HazelcastInstance instance, CacheProxy<String, String> proxy, String value, int partitionId, int count) { for (int i = 0; i < count; i++) { String key = generateKeyForPartition(instance, partitionId); proxy.put(key, value); } } }