/* * 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; import com.hazelcast.config.Config; import com.hazelcast.config.InMemoryFormat; import com.hazelcast.config.NearCacheConfig; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.IMap; import com.hazelcast.test.HazelcastParallelClassRunner; 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.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import static com.hazelcast.util.JVMUtil.REFERENCE_COST_IN_BYTES; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelTest.class}) public class EntryCostEstimatorTest extends HazelcastTestSupport { protected TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); // the JVM-independent portion of the cost of Integer key + Long value record is 124 bytes // (without taking into account 8 references to key, record and value objects) private static final int JVM_INDEPENDENT_ENTRY_COST_IN_BYTES = 124; // JVM-dependent total cost of entry private static final int ENTRY_COST_IN_BYTES = JVM_INDEPENDENT_ENTRY_COST_IN_BYTES + 9 * REFERENCE_COST_IN_BYTES; @Test public void smoke() { SizeEstimatorTestMapBuilder<Long, Long> testMapBuilder = new SizeEstimatorTestMapBuilder<Long, Long>(factory); testMapBuilder.withNodeCount(1).withBackupCount(0).build(getConfig()); assertEquals(0, testMapBuilder.totalHeapCost()); } @Test public void testSinglePut() { SizeEstimatorTestMapBuilder<Integer, Long> testMapBuilder = new SizeEstimatorTestMapBuilder<Integer, Long>(factory); IMap<Integer, Long> map = testMapBuilder.withNodeCount(1).withBackupCount(0).build(getConfig()); map.put(0, 10L); assertEquals(ENTRY_COST_IN_BYTES, testMapBuilder.totalHeapCost()); } @Test public void testExactHeapCostAfterUpdateWithMultipleBackupNodes() { int putCount = 1; int nodeCount = 1; SizeEstimatorTestMapBuilder<Integer, Long> testMapBuilder = new SizeEstimatorTestMapBuilder<Integer, Long>(factory); IMap<Integer, Long> map = testMapBuilder.withNodeCount(nodeCount).withBackupCount(nodeCount - 1).build(getConfig()); for (int i = 0; i < putCount; i++) { map.put(i, System.currentTimeMillis()); } long heapCost = testMapBuilder.totalHeapCost(); assertEquals("Heap cost calculation is wrong!", ENTRY_COST_IN_BYTES * putCount * nodeCount, heapCost); } @Test public void testPutRemoveWithTwoNodeOwnerAndBackup() { String name = randomString(); Config config = getConfig(); config.getMapConfig(name).setBackupCount(1); HazelcastInstance h[] = factory.newInstances(config); warmUpPartitions(h); // create map IMap<String, String> map1 = h[0].getMap(name); IMap<String, String> map2 = h[1].getMap(name); // calculate initial heap costs long map1Cost = map1.getLocalMapStats().getHeapCost(); long map2Cost = map2.getLocalMapStats().getHeapCost(); // check initial costs if zero assertEquals("map1 initial heap cost must be zero..." + map1Cost, 0, map1Cost); assertEquals("map2 initial heap cost must be zero..." + map2Cost, 0, map2Cost); // populate map map1.put("key", "value"); // get sizes long map1Size = map1.size(); long map2Size = map2.size(); // check sizes assertEquals("map1 size must be one..." + map1Size, 1, map1Size); assertEquals("map2 size must be one..." + map2Size, 1, map2Size); // calculate costs map1Cost = map1.getLocalMapStats().getHeapCost(); map2Cost = map2.getLocalMapStats().getHeapCost(); // costs should not be zero assertTrue("map1 cost should be greater than zero....: " + map1Cost, map1Cost > 0); assertTrue("map2 cost should be greater than zero....: " + map2Cost, map2Cost > 0); // one map is backup, so backup & owner cost must be same assertEquals(map1Cost, map2Cost); // remove key map1.remove("key"); // get sizes map1Size = map1.size(); map2Size = map2.size(); // check if sizes are zero assertEquals("map1 size must be zero..." + map1Size, 0, map1Size); assertEquals("map2 size must be zero..." + map2Size, 0, map2Size); map1Cost = map1.getLocalMapStats().getHeapCost(); map2Cost = map2.getLocalMapStats().getHeapCost(); // costs should be zero assertTrue("map1 cost should zero....: " + map1Cost, map1Cost == 0); assertTrue("map2 cost should zero....: " + map2Cost, map2Cost == 0); } @Test public void testNearCache() { String noNearCacheMapName = randomString(); String nearCachedMapName = randomString(); Config config = getConfig(); NearCacheConfig nearCacheConfig = new NearCacheConfig(); nearCacheConfig.setInMemoryFormat(InMemoryFormat.BINARY); config.getMapConfig(nearCachedMapName).setBackupCount(0).setNearCacheConfig(nearCacheConfig); config.getMapConfig(noNearCacheMapName).setBackupCount(0); HazelcastInstance h[] = factory.newInstances(config); warmUpPartitions(h); IMap<String, String> noNearCached = h[0].getMap(noNearCacheMapName); for (int i = 0; i < 1000; i++) { noNearCached.put("key" + i, "value" + i); } IMap<String, String> nearCachedMap = h[0].getMap(nearCachedMapName); for (int i = 0; i < 1000; i++) { nearCachedMap.put("key" + i, "value" + i); } for (int i = 0; i < 1000; i++) { nearCachedMap.get("key" + i); } assertTrue(nearCachedMap.getLocalMapStats().getHeapCost() > noNearCached.getLocalMapStats().getHeapCost()); } @Test public void testInMemoryFormats() { String BINARY_MAP = "testBinaryFormat"; String OBJECT_MAP = "testObjectFormat"; Config config = new Config(); config.getMapConfig(BINARY_MAP).setInMemoryFormat(InMemoryFormat.BINARY).setBackupCount(0); config.getMapConfig(OBJECT_MAP).setInMemoryFormat(InMemoryFormat.OBJECT).setBackupCount(0); int n = 2; HazelcastInstance[] h = factory.newInstances(config); warmUpPartitions(h); // populate map IMap<String, String> binaryMap = h[0].getMap(BINARY_MAP); for (int i = 0; i < 1000; i++) { binaryMap.put("key" + i, "value" + i); } IMap<String, String> objectMap = h[0].getMap(OBJECT_MAP); for (int i = 0; i < 1000; i++) { objectMap.put("key" + i, "value" + i); } for (int i = 0; i < n; i++) { assertTrue(h[i].getMap(BINARY_MAP).getLocalMapStats().getHeapCost() > 0); assertEquals(0, h[i].getMap(OBJECT_MAP).getLocalMapStats().getHeapCost()); } // clear map binaryMap.clear(); objectMap.clear(); for (int i = 0; i < n; i++) { assertEquals(0, h[i].getMap(BINARY_MAP).getLocalMapStats().getHeapCost()); assertEquals(0, h[i].getMap(OBJECT_MAP).getLocalMapStats().getHeapCost()); } } private static class SizeEstimatorTestMapBuilder<K, V> { private String mapName = randomMapName("default"); private HazelcastInstance[] nodes; private int nodeCount; private int backupCount; private TestHazelcastInstanceFactory instanceFactory; SizeEstimatorTestMapBuilder(TestHazelcastInstanceFactory factory) { this.instanceFactory = factory; } SizeEstimatorTestMapBuilder<K, V> mapName(String mapName) { if (mapName == null) { throw new IllegalArgumentException("mapName is null"); } this.mapName = mapName; return this; } SizeEstimatorTestMapBuilder<K, V> withNodeCount(int nodeCount) { if (nodeCount < 1) { throw new IllegalArgumentException("nodeCount < 1"); } this.nodeCount = nodeCount; this.nodes = new HazelcastInstance[nodeCount]; return this; } SizeEstimatorTestMapBuilder<K, V> withBackupCount(int backupCount) { if (backupCount < 0) { throw new IllegalArgumentException("backupCount < 1"); } this.backupCount = backupCount; return this; } IMap<K, V> build(Config config) { if (backupCount > nodeCount - 1) { throw new IllegalArgumentException("backupCount > nodeCount - 1"); } config.getMapConfig(mapName).setBackupCount(backupCount); nodes = instanceFactory.newInstances(config, nodeCount); return nodes[0].getMap(mapName); } long totalHeapCost() { long heapCost = 0L; for (int i = 0; i < nodeCount; i++) { heapCost += nodes[i].getMap(mapName).getLocalMapStats().getHeapCost(); } return heapCost; } } }