/*
* 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.stats;
import com.hazelcast.cache.CacheStatistics;
import com.hazelcast.cache.CacheTestSupport;
import com.hazelcast.cache.ICache;
import com.hazelcast.config.CacheConfig;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.test.AssertTask;
import com.hazelcast.test.HazelcastParallelClassRunner;
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 javax.cache.CacheManager;
import javax.cache.spi.CachingProvider;
import java.util.concurrent.Callable;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@RunWith(HazelcastParallelClassRunner.class)
@Category({QuickTest.class, ParallelTest.class})
public class CacheStatsTest extends CacheTestSupport {
protected TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory();
@Override
protected void onSetup() {
}
@Override
protected void onTearDown() {
factory.terminateAll();
}
@Override
protected HazelcastInstance getHazelcastInstance() {
return factory.newHazelcastInstance(createConfig());
}
@Test
public void testGettingStatistics() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
assertNotNull(stats);
}
@Test
public void testStatisticsDisabled() {
long now = System.currentTimeMillis();
CacheConfig cacheConfig = createCacheConfig();
cacheConfig.setStatisticsEnabled(false);
ICache<Integer, String> cache = createCache(cacheConfig);
CacheStatistics stats = cache.getLocalCacheStatistics();
assertTrue(stats.getCreationTime() >= now);
final int ENTRY_COUNT = 100;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.get(i);
}
for (int i = 0; i < 10; i++) {
cache.remove(i);
}
assertEquals(0, stats.getCacheHits());
assertEquals(0, stats.getCacheHitPercentage(), 0.0f);
assertEquals(0, stats.getCacheMisses());
assertEquals(0, stats.getCacheMissPercentage(), 0.0f);
assertEquals(0, stats.getCacheGets());
assertEquals(0, stats.getAverageGetTime(), 0.0f);
assertEquals(0, stats.getCachePuts());
assertEquals(0, stats.getAveragePutTime(), 0.0f);
assertEquals(0, stats.getCacheRemovals());
assertEquals(0, stats.getAverageRemoveTime(), 0.0f);
assertEquals(0, stats.getLastAccessTime());
assertEquals(0, stats.getLastUpdateTime());
}
@Test
public void testCreationTime() {
long now = System.currentTimeMillis();
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
assertTrue(stats.getCreationTime() >= now);
}
@Test
public void testPutStat() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
final int ENTRY_COUNT = 100;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
assertEquals(ENTRY_COUNT, stats.getCachePuts());
}
@Test
public void testPutStat_whenPutAsync() throws Exception {
ICache<Integer, String> cache = createCache();
final CacheStatistics stats = cache.getLocalCacheStatistics();
final long ENTRY_COUNT = 100;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.putAsync(i, "Value-" + i).get();
}
assertEqualsEventually(new Callable<Long>() {
@Override
public Long call()
throws Exception {
return stats.getCachePuts();
}
}, ENTRY_COUNT);
}
@Test
public void testAveragePutTimeStat() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
final int ENTRY_COUNT = 100;
long start = System.nanoTime();
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
long end = System.nanoTime();
float avgPutTime = NANOSECONDS.toMicros(end - start);
assertTrue(stats.getAveragePutTime() > 0);
assertTrue(stats.getAveragePutTime() < avgPutTime);
}
@Test
public void testGetStat() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
final int ENTRY_COUNT = 100;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
for (int i = 0; i < 2 * ENTRY_COUNT; i++) {
cache.get(i);
}
assertEquals(2 * ENTRY_COUNT, stats.getCacheGets());
}
@Test
public void testGetStat_whenGetAsync() throws Exception {
ICache<Integer, String> cache = createCache();
final CacheStatistics stats = cache.getLocalCacheStatistics();
final long ENTRY_COUNT = 100;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
for (int i = 0; i < 2 * ENTRY_COUNT; i++) {
cache.getAsync(i).get();
}
assertEqualsEventually(new Callable<Long>() {
@Override
public Long call()
throws Exception {
return stats.getCacheGets();
}
}, 2 * ENTRY_COUNT);
}
@Test
public void testAverageGetTimeStat() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
final int ENTRY_COUNT = 100;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
long start = System.nanoTime();
for (int i = 0; i < 2 * ENTRY_COUNT; i++) {
cache.get(i);
}
long end = System.nanoTime();
float avgGetTime = NANOSECONDS.toMicros(end - start);
assertTrue(stats.getAverageGetTime() > 0);
assertTrue(stats.getAverageGetTime() < avgGetTime);
}
@Test
public void testRemoveStat() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
final int ENTRY_COUNT = 100;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
for (int i = 0; i < 2 * ENTRY_COUNT; i++) {
cache.remove(i);
}
assertEquals(ENTRY_COUNT, stats.getCacheRemovals());
}
@Test
public void testRemoveStat_whenRemoveAsync() throws Exception {
ICache<Integer, String> cache = createCache();
final CacheStatistics stats = cache.getLocalCacheStatistics();
final long ENTRY_COUNT = 100;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
for (int i = 0; i < 2 * ENTRY_COUNT; i++) {
cache.removeAsync(i).get();
}
assertEqualsEventually(new Callable<Long>() {
@Override
public Long call()
throws Exception {
return stats.getCacheRemovals();
}
}, ENTRY_COUNT);
}
@Test
public void testAverageRemoveTimeStat() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
final int ENTRY_COUNT = 100;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
long start = System.nanoTime();
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.remove(i);
}
long end = System.nanoTime();
float avgRemoveTime = NANOSECONDS.toMicros(end - start);
assertTrue(stats.getAverageRemoveTime() > 0);
assertTrue(stats.getAverageRemoveTime() < avgRemoveTime);
float currentAverageRemoveTime = stats.getAverageRemoveTime();
sleepAtLeastMillis(1);
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.remove(i);
}
// Latest removes has no effect since keys are already removed at previous loop
assertEquals(currentAverageRemoveTime, stats.getAverageRemoveTime(), 0.0f);
}
@Test
public void testHitStat() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
final int ENTRY_COUNT = 100;
final int GET_COUNT = 3 * ENTRY_COUNT;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
for (int i = 0; i < GET_COUNT; i++) {
cache.get(i);
}
assertEquals(ENTRY_COUNT, stats.getCacheHits());
}
@Test
public void testHitStat_whenGetAsync() throws Exception {
ICache<Integer, String> cache = createCache();
final CacheStatistics stats = cache.getLocalCacheStatistics();
final long ENTRY_COUNT = 100;
final long GET_COUNT = 3 * ENTRY_COUNT;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
for (int i = 0; i < GET_COUNT; i++) {
cache.getAsync(i).get();
}
assertEqualsEventually(new Callable<Long>() {
@Override
public Long call()
throws Exception {
return stats.getCacheHits();
}
}, ENTRY_COUNT);
}
@Test
public void testHitPercentageStat() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
final int ENTRY_COUNT = 100;
final int GET_COUNT = 3 * ENTRY_COUNT;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
for (int i = 0; i < GET_COUNT; i++) {
cache.get(i);
}
float expectedHitPercentage = (float) ENTRY_COUNT / GET_COUNT * 100.0f;
assertEquals(expectedHitPercentage, stats.getCacheHitPercentage(), 0.0f);
}
@Test
public void testMissStat() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
final int ENTRY_COUNT = 100;
final int GET_COUNT = 3 * ENTRY_COUNT;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
for (int i = 0; i < GET_COUNT; i++) {
cache.get(i);
}
assertEquals(GET_COUNT - ENTRY_COUNT, stats.getCacheMisses());
}
@Test
public void testMissStat_whenGetAsync() throws Exception {
ICache<Integer, String> cache = createCache();
final CacheStatistics stats = cache.getLocalCacheStatistics();
final long ENTRY_COUNT = 100;
final long GET_COUNT = 3 * ENTRY_COUNT;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
for (int i = 0; i < GET_COUNT; i++) {
cache.getAsync(i).get();
}
assertEqualsEventually(new Callable<Long>() {
@Override
public Long call()
throws Exception {
return stats.getCacheMisses();
}
}, GET_COUNT - ENTRY_COUNT);
}
public void testMissPercentageStat() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
final int ENTRY_COUNT = 100;
final int GET_COUNT = 3 * ENTRY_COUNT;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
for (int i = 0; i < GET_COUNT; i++) {
cache.get(i);
}
float expectedMissPercentage = (float) (GET_COUNT - ENTRY_COUNT) / GET_COUNT * 100.0f;
assertEquals(expectedMissPercentage, stats.getCacheMissPercentage(), 0.0f);
}
@Test
public void testLastAccessTimeStat() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
final int ENTRY_COUNT = 100;
long start, end;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
assertEquals(0, stats.getLastAccessTime());
start = System.currentTimeMillis();
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.get(i);
}
end = System.currentTimeMillis();
// Hits effect last access time
assertTrue(stats.getLastAccessTime() >= start);
assertTrue(stats.getLastAccessTime() <= end);
long currentLastAccessTime = stats.getLastAccessTime();
sleepAtLeastMillis(1);
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.remove(i);
}
// Removes has no effect on last access time
assertEquals(currentLastAccessTime, stats.getLastAccessTime());
sleepAtLeastMillis(1);
start = System.currentTimeMillis();
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.get(i);
}
end = System.currentTimeMillis();
// Misses also effect last access time
assertTrue(stats.getLastAccessTime() >= start);
assertTrue(stats.getLastAccessTime() <= end);
}
@Test
public void testLastUpdateTimeStat() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
final int ENTRY_COUNT = 100;
long start, end;
start = System.currentTimeMillis();
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
end = System.currentTimeMillis();
assertTrue(stats.getLastUpdateTime() >= start);
assertTrue(stats.getLastUpdateTime() <= end);
start = System.currentTimeMillis();
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.remove(i);
}
end = System.currentTimeMillis();
assertTrue(stats.getLastUpdateTime() >= start);
assertTrue(stats.getLastUpdateTime() <= end);
long currentLastUpdateTime = stats.getLastUpdateTime();
sleepAtLeastMillis(1);
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.remove(i);
}
// Latest removes has no effect since keys are already removed at previous loop
assertEquals(currentLastUpdateTime, stats.getLastUpdateTime());
}
@Test
public void testOwnedEntryCountWhenThereIsNoBackup() {
doTestForOwnedEntryCount(false, false);
}
@Test
public void testOwnedEntryCountWhenThereAreBackupsOnStaticCluster() {
doTestForOwnedEntryCount(true, false);
}
@Test
public void testOwnedEntryCountWhenThereAreBackupsOnDynamicCluster() {
doTestForOwnedEntryCount(true, true);
}
private void doTestForOwnedEntryCount(boolean useBackups, boolean triggerMigration) {
final String cacheName = randomName();
ICache<Integer, String> cache;
CacheStatistics[] allStats;
HazelcastInstance instance2 = null;
if (useBackups) {
// Create the second instance to store data as backup.
instance2 = getHazelcastInstance();
CachingProvider cp = getCachingProvider(instance2);
CacheManager cm = cp.getCacheManager();
cache = createCache(cacheName);
ICache<Integer, String> c = cm.getCache(cacheName).unwrap(ICache.class);
allStats = new CacheStatistics[]{cache.getLocalCacheStatistics(), c.getLocalCacheStatistics()};
} else {
cache = createCache(cacheName);
allStats = new CacheStatistics[]{cache.getLocalCacheStatistics()};
}
final int ENTRY_COUNT = 100;
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
assertOwnedEntryCount(ENTRY_COUNT, allStats);
if (triggerMigration && instance2 != null) {
// Shutdown the second instance to trigger migration so first instance will be owner of all partitions.
instance2.shutdown();
allStats = new CacheStatistics[]{cache.getLocalCacheStatistics()};
assertOwnedEntryCount(ENTRY_COUNT, allStats);
}
for (int i = 0; i < 10; i++) {
cache.remove(i);
}
assertOwnedEntryCount(ENTRY_COUNT - 10, allStats);
for (int i = 10; i < ENTRY_COUNT; i++) {
cache.remove(i);
}
assertOwnedEntryCount(0, allStats);
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
assertOwnedEntryCount(ENTRY_COUNT, allStats);
if (triggerMigration) {
// Create the second instance to trigger migration
// so the second instance will be owner of some partitions
// and the first instance will lose ownership of some instances.
instance2 = getHazelcastInstance();
CachingProvider cp = getCachingProvider(instance2);
CacheManager cm = cp.getCacheManager();
ICache<Integer, String> c = cm.getCache(cacheName).unwrap(ICache.class);
allStats = new CacheStatistics[]{cache.getLocalCacheStatistics(), c.getLocalCacheStatistics()};
assertOwnedEntryCount(ENTRY_COUNT, allStats);
}
cache.clear();
assertOwnedEntryCount(0, allStats);
for (int i = 0; i < ENTRY_COUNT; i++) {
cache.put(i, "Value-" + i);
}
assertOwnedEntryCount(ENTRY_COUNT, allStats);
cache.destroy();
assertOwnedEntryCount(0, allStats);
}
private long getOwnedEntryCount(CacheStatistics... statsList) {
long ownedEntryCount = 0;
for (CacheStatistics stats : statsList) {
ownedEntryCount += stats.getOwnedEntryCount();
}
return ownedEntryCount;
}
private void assertOwnedEntryCount(final int expectedEntryCount, final CacheStatistics... statsList) {
assertTrueEventually(new AssertTask() {
@Override
public void run() throws Exception {
assertEquals(expectedEntryCount, getOwnedEntryCount(statsList));
}
});
}
@Test
public void testExpirations() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
// TODO Produce a case that triggers expirations and verify the expiration count
// At the moment, we are just verifying that this stats is supported
stats.getCacheEvictions();
}
@Test
public void testEvictions() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
// TODO Produce a case that triggers evictions and verify the eviction count
// At the moment, we are just verifying that this stats is supported
stats.getCacheEvictions();
}
@Test(expected = UnsupportedOperationException.class)
public void testNearCacheStats_availableWhenEnabled() {
ICache<Integer, String> cache = createCache();
CacheStatistics stats = cache.getLocalCacheStatistics();
stats.getNearCacheStatistics();
}
}