blob: 52a11042903f7b5ca37f51e2a66922e9949451e4 [file] [log] [blame]
/*=========================================================================
* Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* one or more patents listed at http://www.pivotal.io/patents.
*=========================================================================
*/
package com.gemstone.gemfire.cache30;
import com.gemstone.gemfire.cache.*;
import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
import dunit.*;
/**
* Tests the {@link CacheStatistics} that are maintained by a {@link
* Region} and a {@link com.gemstone.gemfire.cache.Region.Entry}.
*
* @author David Whitlock
*
* @since 3.0
*/
public class CacheStatisticsDUnitTest extends CacheTestCase {
public CacheStatisticsDUnitTest(String name) {
super(name);
}
//////// Helper Methods
/**
* Asserts that two <code>long</code>s are equal concerning a
* delta.
*/
// public static void assertEquals(long expected, long actual,
// long delta) {
// long difference = Math.abs(expected - actual);
// assertTrue("Expected: " + expected
// + " Actual: " + actual,
// difference <= delta);
// }
public static void assertInRange(long start, long end,
long actual) {
assertTrue("Expected: " + actual + " >= " + start,
actual >= start);
assertTrue("Expected: " + actual + " <= " + end,
actual <= end);
}
//////// Test methods
/**
* Tests that the {@link CacheStatistics#getHitCount hit count} and
* {@link CacheStatistics#getMissCount miss count} are updated
* properly for a local region and its entries.
*/
public void testHitMissCount() throws CacheException {
String name = this.getUniqueName();
Object key = "KEY"; // value exists
Object key2 = "KEY2"; // no entry
Object key3 = "KEY3"; // entry, invalid
Object value = "VALUE";
AttributesFactory factory = new AttributesFactory();
factory.setScope(Scope.LOCAL);
factory.setStatisticsEnabled(true);
Region region =
createRegion(name, factory.create());
CacheStatistics rStats = region.getStatistics();
assertEquals(0, rStats.getHitCount());
assertEquals(0, rStats.getMissCount());
assertEquals(0.0f, rStats.getHitRatio(), 0.0f);
assertNull(region.get(key));
assertEquals(0, rStats.getHitCount());
assertEquals(1, rStats.getMissCount());
assertEquals(0.0f, rStats.getHitRatio(), 0.0f);
assertNull(region.get(key));
assertEquals(0, rStats.getHitCount());
assertEquals(2, rStats.getMissCount());
assertEquals(0.0f, rStats.getHitRatio(), 0.0f);
rStats.resetCounts();
assertEquals(0, rStats.getHitCount());
assertEquals(0, rStats.getMissCount());
assertEquals(0.0f, rStats.getHitRatio(), 0.0f);
region.put(key, value);
assertEquals(0, rStats.getHitCount());
assertEquals(0, rStats.getMissCount());
CacheStatistics eStats = region.getEntry(key).getStatistics();
assertEquals(0, eStats.getHitCount());
assertEquals(0, eStats.getMissCount());
assertEquals(0.0f, eStats.getHitRatio(), 0.0f);
region.get(key);
assertEquals(1, eStats.getHitCount());
assertEquals(0, eStats.getMissCount());
assertEquals(1.0f, eStats.getHitRatio(), 0.0f);
assertEquals(1, rStats.getHitCount());
assertEquals(0, rStats.getMissCount());
assertEquals(1.0f, rStats.getHitRatio(), 0.0f);
region.get(key2);
// assert independent from eStats
assertEquals(1, eStats.getHitCount());
assertEquals(0, eStats.getMissCount());
assertEquals(1.0f, eStats.getHitRatio(), 0.0f);
assertEquals(1, rStats.getHitCount());
assertEquals(1, rStats.getMissCount());
assertEquals(0.5f, rStats.getHitRatio(), 0.0f);
region.create(key3, null);
CacheStatistics e3Stats = region.getEntry(key3).getStatistics();
assertEquals(0, e3Stats.getHitCount());
assertEquals(0, e3Stats.getMissCount());
assertEquals(0.0f, e3Stats.getHitRatio(), 0.0f);
region.get(key3); // miss on existing entry
assertEquals(0, e3Stats.getHitCount());
assertEquals(1, e3Stats.getMissCount());
assertEquals(0.0f, e3Stats.getHitRatio(), 0.0f);
assertEquals(1, rStats.getHitCount());
assertEquals(2, rStats.getMissCount());
assertEquals(0.33f, rStats.getHitRatio(), 0.01f);
eStats.resetCounts();
assertEquals(0, eStats.getHitCount());
assertEquals(0, eStats.getMissCount());
assertEquals(0.0f, eStats.getHitRatio(), 0.0f);
rStats.resetCounts();
region.invalidate(key);
assertEquals(0, eStats.getHitCount());
assertEquals(0, eStats.getMissCount());
assertEquals(0.0f, eStats.getHitRatio(), 0.0f);
region.get(key);
assertEquals(0, eStats.getHitCount());
assertEquals(1, eStats.getMissCount());
assertEquals(0, rStats.getHitCount());
assertEquals(1, rStats.getMissCount());
region.destroy(key);
try {
eStats.getMissCount();
fail("Should have thrown an EntryDestroyedException");
} catch (EntryDestroyedException ex) {
// pass...
}
}
/**
* Tests that the {@linkplain CacheStatistics#getLastAccessedTime
* last access time} and {@link CacheStatistics#getLastModifiedTime
* last modified time} are update appropriately for a local region
* and its entries. It also validates that the last modification
* and last access times are propagated to parent regions.
*/
public void testTimeStats() throws CacheException, InterruptedException
{
final long ESTAT_RES = 100; // the resolution, in ms, of entry stats
String name = this.getUniqueName();
Object key = "KEY";
Object key2 = "KEY2";
Object value = "VALUE";
long before;
long after;
long oldBefore;
long oldAfter;
AttributesFactory factory = new AttributesFactory();
factory.setScope(Scope.LOCAL);
factory.setStatisticsEnabled(true);
before = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
Region region =
createRegion(name, factory.create());
CacheStatistics rStats = region.getStatistics();
CacheStatistics rootStats =
getRootRegion().getStatistics();
after = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
assertInRange(before, after, rStats.getLastAccessedTime());
assertInRange(before, after, rStats.getLastModifiedTime());
assertInRange(before, after, rootStats.getLastAccessedTime());
assertInRange(before, after, rootStats.getLastModifiedTime());
oldBefore = before;
oldAfter = after;
pause(150);
before = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
region.get(key);
after = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
assertInRange(before, after, rStats.getLastAccessedTime());
assertInRange(oldBefore, oldAfter, rStats.getLastModifiedTime());
assertInRange(before, after, rootStats.getLastAccessedTime());
assertInRange(oldBefore, oldAfter, rootStats.getLastModifiedTime());
pause(150);
before = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
region.put(key, value);
CacheStatistics eStats = region.getEntry(key).getStatistics();
after = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
assertInRange(before, after, rStats.getLastAccessedTime());
assertInRange(before, after, rStats.getLastModifiedTime());
assertInRange(before, after, rootStats.getLastAccessedTime());
assertInRange(before, after, rootStats.getLastModifiedTime());
assertInRange(before-ESTAT_RES, after+ESTAT_RES, eStats.getLastAccessedTime());
assertInRange(before-ESTAT_RES, after+ESTAT_RES, eStats.getLastModifiedTime());
oldBefore = before;
oldAfter = after;
pause(150);
before = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
region.get(key);
after = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
assertInRange(before, after, rStats.getLastAccessedTime());
assertInRange(oldBefore, oldAfter, rStats.getLastModifiedTime());
assertInRange(before, after, rootStats.getLastAccessedTime());
assertInRange(oldBefore, oldAfter, rootStats.getLastModifiedTime());
assertInRange(before-ESTAT_RES, after+ESTAT_RES, eStats.getLastAccessedTime());
assertInRange(oldBefore-ESTAT_RES, oldAfter+ESTAT_RES, eStats.getLastModifiedTime());
long oldOldBefore = oldBefore;
long oldOldAfter = oldAfter;
oldBefore = before;
oldAfter = after;
pause(150);
before = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
region.create(key2, null);
CacheStatistics eStats2 = region.getEntry(key2).getStatistics();
after = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
assertInRange(before, after, rStats.getLastAccessedTime());
assertInRange(before, after, rStats.getLastModifiedTime());
assertInRange(before, after, rootStats.getLastAccessedTime());
assertInRange(before, after, rootStats.getLastModifiedTime());
assertInRange(oldBefore-ESTAT_RES, oldAfter+ESTAT_RES, eStats.getLastAccessedTime());
assertInRange(oldOldBefore-ESTAT_RES, oldOldAfter+ESTAT_RES, eStats.getLastModifiedTime());
assertInRange(before-ESTAT_RES, after+ESTAT_RES, eStats2.getLastAccessedTime());
assertInRange(before-ESTAT_RES, after+ESTAT_RES, eStats2.getLastModifiedTime());
// Invalidation and destruction do not update the modification/access
// times
oldBefore = before;
oldAfter = after;
pause(150);
before = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
region.invalidate(key2);
after = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
assertInRange(oldBefore-ESTAT_RES, oldAfter+ESTAT_RES, eStats2.getLastAccessedTime());
assertInRange(oldBefore-ESTAT_RES, oldAfter+ESTAT_RES, eStats2.getLastModifiedTime());
assertInRange(oldBefore, oldAfter, rStats.getLastAccessedTime());
assertInRange(oldBefore, oldAfter, rStats.getLastModifiedTime());
assertInRange(oldBefore, oldAfter, rootStats.getLastAccessedTime());
assertInRange(oldBefore, oldAfter, rootStats.getLastModifiedTime());
pause(150);
before = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
region.destroy(key2);
after = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
assertInRange(oldBefore, oldAfter, rStats.getLastAccessedTime());
assertInRange(oldBefore, oldAfter, rStats.getLastModifiedTime());
assertInRange(oldBefore, oldAfter, rootStats.getLastAccessedTime());
assertInRange(oldBefore, oldAfter, rootStats.getLastModifiedTime());
before = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
Region sub =
region.createSubregion("sub", region.getAttributes());
after = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
assertInRange(before, after, rStats.getLastAccessedTime());
assertInRange(before, after, rStats.getLastModifiedTime());
assertInRange(before, after, rootStats.getLastAccessedTime());
assertInRange(before, after, rootStats.getLastModifiedTime());
oldBefore = before;
oldAfter = after;
before = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
sub.destroyRegion();
after = ((GemFireCacheImpl)getCache()).cacheTimeMillis();
assertInRange(oldBefore, oldAfter, rStats.getLastAccessedTime());
assertInRange(oldBefore, oldAfter, rStats.getLastModifiedTime());
assertInRange(oldBefore, oldAfter, rootStats.getLastAccessedTime());
assertInRange(oldBefore, oldAfter, rootStats.getLastModifiedTime());
}
/** The last time an entry was accessed */
protected static volatile long lastAccessed;
protected static volatile long lastModified;
protected static volatile long lastModifiedLocal;
protected static volatile long lastModifiedRemote;
/**
* Tests that distributed operations update the {@link
* CacheStatistics#getLastModifiedTime last modified time}, but not
* the {@link CacheStatistics#getLastAccessedTime last accessed
* time}. It also validates that distributed operations do not
* affect the hit and miss counts in remote caches.
*/
public void testDistributedStats() {
final String name = this.getUniqueName();
final Object key = "KEY";
final Object value = "VALUE";
SerializableRunnable create =
new CacheSerializableRunnable("Create Region") {
public void run2() throws CacheException {
AttributesFactory factory = new AttributesFactory();
factory.setScope(Scope.DISTRIBUTED_ACK);
factory.setEarlyAck(false);
factory.setStatisticsEnabled(true);
createRegion(name, factory.create());
}
};
Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
vm0.invoke(create);
vm1.invoke(create);
vm0.invoke(new CacheSerializableRunnable("Define entry") {
public void run2() throws CacheException {
Region region =
getRootRegion().getSubregion(name);
// region.create(key, null);
region.put(key,value);
CacheStatistics stats =
region.getEntry(key).getStatistics();
lastAccessed = stats.getLastAccessedTime();
lastModified = stats.getLastModifiedTime();
getCache().getLogger().fine("DEFINE: lastAccessed: " + lastAccessed + ", lastModified: " + lastModified);
assertEquals(0, stats.getHitCount());
assertEquals(0, stats.getMissCount());
assertTrue(lastModified > 0);
lastModifiedLocal = lastModified;
}
});
vm1.invoke(new CacheSerializableRunnable("Net search") {
public void run2() throws CacheException {
Region region =
getRootRegion().getSubregion(name);
Object result = region.get(key);
assertEquals(value,result);
CacheStatistics stats =
region.getEntry(key).getStatistics();
lastModifiedRemote = stats.getLastModifiedTime();
getCache().getLogger().fine("NETSEARCH: lastAccessed: " + stats.getLastAccessedTime() + ", lastModified: " + stats.getLastModifiedTime());
}
});
vm0.invoke(new CacheSerializableRunnable("Verify stats") {
public void run2() throws CacheException {
Region region =
getRootRegion().getSubregion(name);
CacheStatistics stats =
region.getEntry(key).getStatistics();
assertEquals(lastAccessed, stats.getLastAccessedTime());
assertEquals(lastModified, stats.getLastModifiedTime());
assertEquals(0, stats.getHitCount());
assertEquals(0, stats.getMissCount());
}
});
assertEquals(lastModifiedLocal,lastModifiedRemote);
// make sure at least 100ms have passed; otherwise, the update
// may not actually bump the statistics
pause(100);
vm1.invoke(new CacheSerializableRunnable("Update") {
public void run2() throws CacheException {
Region region =
getRootRegion().getSubregion(name);
//assertNull(region.getEntry(key));
region.put(key, value);
assertEquals(value, region.getEntry(key).getValue());
CacheStatistics stats = region.getEntry(key).getStatistics();
getCache().getLogger().fine("UPDATE: lastAccessed: " + stats.getLastAccessedTime() + ", lastModified: " + stats.getLastModifiedTime());
}
});
final long errorMargin = 50;
vm0.invoke(new CacheSerializableRunnable("Verify stats") {
public void run2() throws CacheException {
Region region =
getRootRegion().getSubregion(name);
CacheStatistics stats =
region.getEntry(key).getStatistics();
long ta = stats.getLastAccessedTime();
long tm = stats.getLastModifiedTime();
long hc = stats.getHitCount();
long mc = stats.getMissCount();
getCache().getLogger().fine("VERIFY: lastAccessed: " + ta + ", lastModified: " + tm);
assertTrue("lastAccessedTime was " + ta +
" but was expected to be > " + lastAccessed,
lastAccessed < (ta + errorMargin));
assertTrue("lastAccessed=" + lastAccessed +
" should be < stats.getLastModifiedTime=" + tm,
lastAccessed < (tm + errorMargin));
assertEquals(0, hc);
assertEquals(0, mc);
lastAccessed = stats.getLastAccessedTime();
}
});
vm1.invoke(new CacheSerializableRunnable("Invalidate") {
public void run2() throws CacheException {
Region region =
getRootRegion().getSubregion(name);
region.invalidate(key);
}
});
vm0.invoke(new CacheSerializableRunnable("Verify stats") {
public void run2() throws CacheException {
Region region =
getRootRegion().getSubregion(name);
CacheStatistics stats =
region.getEntry(key).getStatistics();
assertEquals(lastAccessed, stats.getLastAccessedTime());
assertEquals(lastAccessed, stats.getLastModifiedTime());
assertEquals(0, stats.getHitCount());
assertEquals(0, stats.getMissCount());
}
});
vm1.invoke(new CacheSerializableRunnable("Destroy Entry") {
public void run2() throws CacheException {
Region region =
getRootRegion().getSubregion(name);
region.destroy(key);
}
});
vm0.invoke(new CacheSerializableRunnable("Verify region stats") {
public void run2() throws CacheException {
Region region =
getRootRegion().getSubregion(name);
CacheStatistics stats = region.getStatistics();
// lastAccessed var contains stat from an Entry, which may be
// up to 100 ms off from stat in Region because Entry has
// less precision
//assertEquals(lastAccessed, stats.getLastAccessedTime(), 100);
assertEquals(0, stats.getHitCount());
assertEquals(0, stats.getMissCount());
}
});
}
/**
* Tests that an attempt to get statistics when they are disabled
* results in a {@link StatisticsDisabledException}.
*/
public void testDisabledStatistics() throws CacheException {
String name = this.getUniqueName();
Object key = "KEY";
Object value = "VALUE";
AttributesFactory factory = new AttributesFactory();
factory.setStatisticsEnabled(false);
Region region =
createRegion(name, factory.create());
try {
region.getStatistics();
fail("Should have thrown a StatisticsDisabledException");
} catch (StatisticsDisabledException ex) {
// pass...
}
region.put(key, value);
Region.Entry entry = region.getEntry(key);
try {
entry.getStatistics();
} catch (StatisticsDisabledException ex) {
// pass...
}
}
}