/* * 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.cache.impl.nearcache.invalidation; import com.hazelcast.cache.impl.CacheEventHandler; import com.hazelcast.cache.impl.CacheService; import com.hazelcast.cache.impl.HazelcastServerCachingProvider; import com.hazelcast.client.cache.impl.HazelcastClientCachingProvider; import com.hazelcast.client.cache.impl.nearcache.ClientNearCacheTestSupport; import com.hazelcast.client.config.ClientConfig; import com.hazelcast.client.impl.HazelcastClientInstanceImpl; import com.hazelcast.client.impl.HazelcastClientProxy; import com.hazelcast.config.CacheConfig; import com.hazelcast.config.Config; import com.hazelcast.config.InMemoryFormat; import com.hazelcast.config.NearCacheConfig; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.internal.nearcache.impl.invalidation.MetaDataGenerator; import com.hazelcast.internal.partition.InternalPartitionService; 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.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import javax.cache.Cache; import javax.cache.CacheManager; import javax.cache.spi.CachingProvider; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import static com.hazelcast.config.EvictionConfig.MaxSizePolicy.ENTRY_COUNT; import static com.hazelcast.config.InMemoryFormat.BINARY; 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 ClientNearCacheTestSupport { @Override protected Config createConfig() { Config config = super.createConfig(); 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 ClientConfig createClientConfig() { ClientConfig clientConfig = new ClientConfig(); clientConfig.setProperty("hazelcast.invalidation.max.tolerated.miss.count", "0"); return clientConfig; } @Override protected CacheConfig createCacheConfig(InMemoryFormat inMemoryFormat) { CacheConfig cacheConfig = super.createCacheConfig(inMemoryFormat); cacheConfig.getEvictionConfig() .setMaximumSizePolicy(ENTRY_COUNT) .setSize(MAX_VALUE); return cacheConfig; } @Override protected NearCacheConfig createNearCacheConfig(InMemoryFormat inMemoryFormat) { NearCacheConfig nearCacheConfig = super.createNearCacheConfig(inMemoryFormat); nearCacheConfig.setInvalidateOnChange(true) .getEvictionConfig() .setMaximumSizePolicy(ENTRY_COUNT) .setSize(MAX_VALUE); return nearCacheConfig; } @Test public void ensure_nearCachedClient_and_member_data_sync_eventually() throws Exception { final int cacheSize = 100000; final AtomicBoolean stopTest = new AtomicBoolean(); final Config config = createConfig(); final HazelcastInstance member = hazelcastFactory.newHazelcastInstance(config); CachingProvider provider = HazelcastServerCachingProvider.createCachingProvider(serverInstance); CacheManager serverCacheManager = provider.getCacheManager(); // populated from member. final Cache<Integer, Integer> memberCache = serverCacheManager.createCache(DEFAULT_CACHE_NAME, createCacheConfig(BINARY)); for (int i = 0; i < cacheSize; i++) { memberCache.put(i, i); } ClientConfig clientConfig = createClientConfig(); clientConfig.addNearCacheConfig(createNearCacheConfig(BINARY)); HazelcastClientProxy client = (HazelcastClientProxy) hazelcastFactory.newHazelcastClient(clientConfig); CachingProvider clientCachingProvider = HazelcastClientCachingProvider.createCachingProvider(client); final Cache<Integer, Integer> clientCache = clientCachingProvider.getCacheManager().createCache( DEFAULT_CACHE_NAME, createCacheConfig(BINARY)); Thread populateNearCache = new Thread(new Runnable() { public void run() { while (!stopTest.get()) { for (int i = 0; i < cacheSize; i++) { clientCache.get(i); } } } }); Thread distortSequence = new Thread(new Runnable() { @Override public void run() { while (!stopTest.get()) { distortRandomPartitionSequence(DEFAULT_CACHE_NAME, 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(cacheSize); int value = getInt(Integer.MAX_VALUE); memberCache.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 < cacheSize; i++) { Integer valueSeenFromMember = memberCache.get(i); Integer valueSeenFromClient = clientCache.get(i); assertEquals(valueSeenFromMember, valueSeenFromClient); } } }); } private void distortRandomPartitionSequence(String mapName, HazelcastInstance member) { NodeEngineImpl nodeEngineImpl = getNodeEngineImpl(member); CacheService service = nodeEngineImpl.getService(CacheService.SERVICE_NAME); CacheEventHandler cacheEventHandler = service.getCacheEventHandler(); MetaDataGenerator metaDataGenerator = cacheEventHandler.getMetaDataGenerator(); InternalPartitionService partitionService = nodeEngineImpl.getPartitionService(); int partitionCount = partitionService.getPartitionCount(); int randomPartition = getInt(partitionCount); int randomSequence = getInt(MAX_VALUE); metaDataGenerator.setCurrentSequence(mapName, randomPartition, randomSequence); } private void distortRandomPartitionUuid(HazelcastInstance member) { NodeEngineImpl nodeEngineImpl = getNodeEngineImpl(member); int partitionCount = nodeEngineImpl.getPartitionService().getPartitionCount(); CacheService service = nodeEngineImpl.getService(CacheService.SERVICE_NAME); CacheEventHandler cacheEventHandler = service.getCacheEventHandler(); MetaDataGenerator metaDataGenerator = cacheEventHandler.getMetaDataGenerator(); UUID uuid = UuidUtil.newUnsecureUUID(); int randomPartition = getInt(partitionCount); metaDataGenerator.setUuid(randomPartition, uuid); } protected HazelcastClientInstanceImpl getHazelcastClientInstanceImpl(HazelcastInstance client) { HazelcastClientProxy clientProxy = (HazelcastClientProxy) client; return clientProxy.client; } }