/* * 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.client.map.impl.nearcache.invalidation; import com.hazelcast.client.config.ClientConfig; import com.hazelcast.client.impl.HazelcastClientInstanceImpl; import com.hazelcast.client.impl.HazelcastClientProxy; import com.hazelcast.client.test.TestHazelcastFactory; import com.hazelcast.config.Config; import com.hazelcast.config.EvictionPolicy; import com.hazelcast.config.MapConfig; import com.hazelcast.config.NearCacheConfig; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.IMap; import com.hazelcast.internal.nearcache.impl.invalidation.Invalidator; import com.hazelcast.internal.nearcache.impl.invalidation.MetaDataGenerator; import com.hazelcast.internal.partition.InternalPartitionService; import com.hazelcast.map.impl.MapService; import com.hazelcast.map.impl.MapServiceContext; import com.hazelcast.map.impl.nearcache.MapNearCacheManager; import com.hazelcast.map.impl.nearcache.NearCacheTestSupport; import com.hazelcast.spi.impl.NodeEngineImpl; import com.hazelcast.spi.properties.GroupProperty; import com.hazelcast.test.AssertTask; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.annotation.NightlyTest; import com.hazelcast.util.UuidUtil; import org.junit.After; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import java.util.concurrent.atomic.AtomicBoolean; import static com.hazelcast.map.impl.MapService.SERVICE_NAME; import static com.hazelcast.util.RandomPicker.getInt; import static java.lang.Integer.MAX_VALUE; import static org.junit.Assert.assertEquals; @RunWith(HazelcastParallelClassRunner.class) @Category(NightlyTest.class) public class InvalidationMetadataDistortionTest extends NearCacheTestSupport { private final TestHazelcastFactory factory = new TestHazelcastFactory(); @After public void tearDown() throws Exception { factory.shutdownAll(); } protected Config createConfig() { Config config = new Config(); config.setProperty(GroupProperty.PARTITION_COUNT.getName(), "271"); config.setProperty(GroupProperty.CACHE_INVALIDATION_MESSAGE_BATCH_ENABLED.getName(), "true"); config.setProperty(GroupProperty.CACHE_INVALIDATION_MESSAGE_BATCH_SIZE.getName(), "10"); return config; } protected MapConfig createMapConfig(String mapName) { MapConfig mapConfig = new MapConfig(mapName); mapConfig.setBackupCount(0); return mapConfig; } protected NearCacheConfig createNearCacheConfig(String mapName) { NearCacheConfig nearCacheConfig = newNearCacheConfig(); nearCacheConfig.setInvalidateOnChange(true); nearCacheConfig.setName(mapName); nearCacheConfig.getEvictionConfig() .setSize(Integer.MAX_VALUE) .setEvictionPolicy(EvictionPolicy.NONE); return nearCacheConfig; } protected ClientConfig createClientConfig() { ClientConfig clientConfig = new ClientConfig(); clientConfig.setProperty("hazelcast.invalidation.max.tolerated.miss.count", "0"); return clientConfig; } @Test public void lostInvalidation() throws Exception { final String mapName = "origin-map"; final int mapSize = 100000; final AtomicBoolean stopTest = new AtomicBoolean(); // members are created. final Config config = createConfig().addMapConfig(createMapConfig(mapName)); final HazelcastInstance member = factory.newHazelcastInstance(config); factory.newHazelcastInstance(config); // map is populated form member. final IMap<Integer, Integer> memberMap = member.getMap(mapName); for (int i = 0; i < mapSize; i++) { memberMap.put(i, i); } // a new client comes. ClientConfig clientConfig = createClientConfig().addNearCacheConfig(createNearCacheConfig(mapName)); final HazelcastInstance client = factory.newHazelcastClient(clientConfig); final IMap<Integer, Integer> clientMap = client.getMap(mapName); Thread populateNearCache = new Thread(new Runnable() { public void run() { while (!stopTest.get()) { for (int i = 0; i < mapSize; i++) { clientMap.get(i); } } } }); Thread distortSequence = new Thread(new Runnable() { @Override public void run() { while (!stopTest.get()) { distortRandomPartitionSequence(mapName, member); sleepSeconds(1); } } }); Thread distortUuid = new Thread(new Runnable() { @Override public void run() { while (!stopTest.get()) { distortRandomPartitionUuid(member); sleepSeconds(5); } } }); Thread put = new Thread(new Runnable() { public void run() { // change some data while (!stopTest.get()) { int key = getInt(mapSize); int value = getInt(Integer.MAX_VALUE); memberMap.put(key, value); sleepAtLeastMillis(100); } } }); // start threads put.start(); populateNearCache.start(); distortSequence.start(); distortUuid.start(); sleepSeconds(60); // stop threads stopTest.set(true); distortUuid.join(); distortSequence.join(); populateNearCache.join(); put.join(); assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { for (int i = 0; i < mapSize; i++) { Integer valueSeenFromMember = memberMap.get(i); Integer valueSeenFromClient = clientMap.get(i); assertEquals(valueSeenFromMember, valueSeenFromClient); } } }); } private void distortRandomPartitionSequence(String mapName, HazelcastInstance member) { NodeEngineImpl nodeEngineImpl = getNodeEngineImpl(member); MapService mapService = nodeEngineImpl.getService(SERVICE_NAME); MapServiceContext mapServiceContext = mapService.getMapServiceContext(); MapNearCacheManager mapNearCacheManager = mapServiceContext.getMapNearCacheManager(); Invalidator invalidator = mapNearCacheManager.getInvalidator(); MetaDataGenerator metaDataGenerator = invalidator.getMetaDataGenerator(); InternalPartitionService partitionService = nodeEngineImpl.getPartitionService(); int partitionCount = partitionService.getPartitionCount(); metaDataGenerator.setCurrentSequence(mapName, getInt(partitionCount), getInt(MAX_VALUE)); } private void distortRandomPartitionUuid(HazelcastInstance member) { NodeEngineImpl nodeEngineImpl = getNodeEngineImpl(member); int partitionCount = nodeEngineImpl.getPartitionService().getPartitionCount(); int partitionId = getInt(partitionCount); MapService mapService = nodeEngineImpl.getService(SERVICE_NAME); MapServiceContext mapServiceContext = mapService.getMapServiceContext(); MapNearCacheManager mapNearCacheManager = mapServiceContext.getMapNearCacheManager(); Invalidator invalidator = mapNearCacheManager.getInvalidator(); MetaDataGenerator metaDataGenerator = invalidator.getMetaDataGenerator(); metaDataGenerator.setUuid(partitionId, UuidUtil.newUnsecureUUID()); } protected HazelcastClientInstanceImpl getHazelcastClientInstanceImpl(HazelcastInstance client) { HazelcastClientProxy clientProxy = (HazelcastClientProxy) client; return clientProxy.client; } }