blob: 9c12fff1f6afc970d7e456d808e4682fa3209866 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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 org.apache.geode.cache30;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.apache.geode.cache.AttributesFactory;
import org.apache.geode.cache.CacheException;
import org.apache.geode.cache.CacheStatistics;
import org.apache.geode.cache.PartitionAttributesFactory;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.StatisticsDisabledException;
import org.apache.geode.internal.cache.InternalRegion;
import org.apache.geode.test.dunit.SerializableRunnableIF;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.cache.internal.JUnit4CacheTestCase;
/**
* Tests the {@link CacheStatistics} that are maintained by a partitioned {@link Region} .
*
*
* @since GemFire 3.0
*/
public class CacheStatisticsPartitionedRegionDUnitTest extends JUnit4CacheTestCase {
public CacheStatisticsPartitionedRegionDUnitTest() {
super();
}
//////// Helper Methods
/**
* Asserts that two <code>long</code>s are equal concerning a 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 partitioned region
* with only one server.
*/
@Test
public void testHitMissCount() throws CacheException {
String name = getUniqueName();
Object key = "KEY"; // value exists
Object key2 = "KEY2"; // no entry
Object key3 = "KEY3"; // entry, invalid
Object value = "VALUE";
AttributesFactory factory = new AttributesFactory();
factory.setStatisticsEnabled(true);
Region region = createPartitionedRegion(name, factory.create());
CacheStatistics rStats = region.getStatistics();
assertEquals(0, rStats.getHitCount());
assertEquals(0, rStats.getMissCount());
assertEquals(0.0f, rStats.getHitRatio(), 0.0f);
region.get(key);
assertEquals(1, rStats.getMissCount());
assertEquals(0, rStats.getHitCount());
assertEquals(0.0f, rStats.getHitRatio(), 0.0f);
region.get(key);
assertEquals(2, rStats.getMissCount());
assertEquals(0, rStats.getHitCount());
assertEquals(0.0f, rStats.getHitRatio(), 0.0f);
rStats.resetCounts();
assertEquals(0, rStats.getMissCount());
assertEquals(0, rStats.getHitCount());
assertEquals(0.0f, rStats.getHitRatio(), 0.0f);
region.put(key, value);
assertEquals(0, rStats.getHitCount());
assertEquals(0, rStats.getMissCount());
assertEquals(0.0f, rStats.getHitRatio(), 0.0f);
region.get(key);
assertEquals(1, rStats.getHitCount());
assertEquals(0, rStats.getMissCount());
assertEquals(1.0f, rStats.getHitRatio(), 0.0f);
region.get(key2);
assertEquals(1, rStats.getMissCount());
assertEquals(1, rStats.getHitCount());
assertEquals(0.5f, rStats.getHitRatio(), 0.0f);
region.create(key3, null);
region.get(key3); // miss on existing entry
assertEquals(2, rStats.getMissCount());
assertEquals(1, rStats.getHitCount());
assertEquals(0.33f, rStats.getHitRatio(), 0.01f);
rStats.resetCounts();
assertEquals(0, rStats.getMissCount());
assertEquals(0, rStats.getHitCount());
assertEquals(0.0f, rStats.getHitRatio(), 0.0f);
region.invalidate(key);
region.get(key);
assertEquals(0, rStats.getHitCount());
assertEquals(1, rStats.getMissCount());
assertEquals(0.0f, rStats.getHitRatio(), 0.0f);
}
/**
* Tests that the {@linkplain CacheStatistics#getLastAccessedTime last access time} and
* {@link CacheStatistics#getLastModifiedTime last modified time} are updated appropriately for a
* partitioned region with only one server.
*/
@Test
public void testTimeStats() throws CacheException, InterruptedException {
final long ESTAT_RES = 100; // the resolution, in ms, of entry stats
String name = getUniqueName();
Object key1 = 1;
Object key2 = 2;
Object missingKey = 999;
Object value = "VALUE";
long before;
long after;
long oldBefore;
long oldAfter;
AttributesFactory factory = new AttributesFactory();
// factory.setScope(Scope.LOCAL);
factory.setStatisticsEnabled(true);
Region region = createPartitionedRegion(name, factory.create());
CacheStatistics rStats = region.getStatistics();
assertEquals(0, rStats.getLastAccessedTime());
assertEquals(0, rStats.getLastModifiedTime());
before = ((InternalRegion) region).cacheTimeMillis();
// When the get is invoked, the lastAccessedTime and lastModified time
// are updated as a new BucketRegion is created.
region.get(missingKey);
after = ((InternalRegion) region).cacheTimeMillis();
assertInRange(before, after, rStats.getLastAccessedTime());
assertInRange(before, after, rStats.getLastModifiedTime());
oldBefore = before;
oldAfter = after;
waitForClockToChange(region);
before = ((InternalRegion) region).cacheTimeMillis();
region.get(missingKey);
after = ((InternalRegion) region).cacheTimeMillis();
assertInRange(before, after, rStats.getLastAccessedTime());
assertInRange(oldBefore, oldAfter, rStats.getLastModifiedTime());
waitForClockToChange(region);
before = ((InternalRegion) region).cacheTimeMillis();
region.put(key1, value);
CacheStatistics eStats = region.getEntry(key1).getStatistics();
after = ((InternalRegion) region).cacheTimeMillis();
assertInRange(before, after, rStats.getLastModifiedTime());
assertInRange(before, after, rStats.getLastAccessedTime());
assertInRange(before - ESTAT_RES, after + ESTAT_RES, eStats.getLastAccessedTime());
assertInRange(before - ESTAT_RES, after + ESTAT_RES, eStats.getLastModifiedTime());
oldBefore = before;
oldAfter = after;
waitForClockToChange(region);
before = ((InternalRegion) region).cacheTimeMillis();
region.get(key1);
// eStats must be obtained again. The previous object is not updated.
// Seems it just provides a snapshot at the moment it is received.
eStats = region.getEntry(key1).getStatistics();
after = ((InternalRegion) region).cacheTimeMillis();
assertInRange(before, after, rStats.getLastAccessedTime());
assertInRange(oldBefore, oldAfter, rStats.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;
waitForClockToChange(region, ESTAT_RES);
before = ((InternalRegion) region).cacheTimeMillis();
region.create(key2, null);
region.get(key2);
CacheStatistics eStats2 = region.getEntry(key2).getStatistics();
after = ((InternalRegion) region).cacheTimeMillis();
assertInRange(before, after, rStats.getLastModifiedTime());
assertInRange(before, after, rStats.getLastAccessedTime());
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 does not update the modification/access
// times
region.invalidate(key2);
assertInRange(before, after, rStats.getLastModifiedTime());
assertInRange(before - ESTAT_RES, after + ESTAT_RES, eStats2.getLastAccessedTime());
assertInRange(before - ESTAT_RES, after + ESTAT_RES, eStats2.getLastModifiedTime());
assertInRange(before, after, rStats.getLastAccessedTime());
region.destroy(key2);
assertInRange(before, after, rStats.getLastModifiedTime());
assertInRange(before, after, rStats.getLastAccessedTime());
}
private void waitForClockToChange(Region region) {
waitForClockToChange(region, 0);
}
/**
* Waits for the time in the <code>region</code> to step up
* beyond the current time in the region plus the number of
* milliseconds passed in the <code>millisecs</code> argument.
*/
private void waitForClockToChange(Region region, long millisecs) {
long time = ((InternalRegion) region).cacheTimeMillis();
while (time >= ((InternalRegion) region).cacheTimeMillis() + millisecs) {
}
}
/** The last time an entry was accessed */
protected static volatile long lastAccessed;
protected static volatile long lastModified;
/**
* Tests that the {@link CacheStatistics#getHitCount hit count},
* {@link CacheStatistics#getMissCount miss count},
* {@linkplain CacheStatistics#getLastAccessedTime last access time}
* and {@link CacheStatistics#getLastModifiedTime last modified time} are updated
* properly for a partitioned region
* with more than one server.
*/
@Test
public void testDistributedStats() {
final String name = getUniqueName();
final Object key0 = 0;
final Object key1 = 1;
final Object key2 = 2;
final Object key3 = 3;
final Object value = "VALUE";
final Object value1 = "VALUE1";
final Object value2 = "VALUE2";
final Object value3 = "VALUE3";
final Object notPresentKey1 = "NOT_PRESENT_1";
final Object notPresentKey2 = "NOT_PRESENT_2";
final Object notPresentKey3 = "NOT_PRESENT_3";
final Object notPresentKey4 = "NOT_PRESENT_4";
VM vm0 = VM.getVM(0);
VM vm1 = VM.getVM(1);
SerializableRunnableIF create = () -> {
AttributesFactory factory = new AttributesFactory();
factory.setEarlyAck(false);
factory.setStatisticsEnabled(true);
createPartitionedRegion(name, factory.create());
};
vm0.invoke(create);
vm1.invoke(create);
vm0.invoke(() -> {
Region region = getRootRegion(name);
CacheStatistics stats = region.getStatistics();
region.put(key0, value);
region.put(key1, value1);
assertTrue(stats.getLastModifiedTime() > lastModified);
assertEquals(0, stats.getHitCount());
assertEquals(0, stats.getMissCount());
});
vm1.invoke(() -> {
Region region = getRootRegion(name);
CacheStatistics stats = region.getStatistics();
region.put(key2, value3);
region.put(key3, value3);
assertTrue(stats.getLastModifiedTime() > lastModified);
assertEquals(0, stats.getHitCount());
assertEquals(0, stats.getMissCount());
});
vm0.invoke(() -> {
Region region = getRootRegion(name);
CacheStatistics stats = region.getStatistics();
lastAccessed = stats.getLastAccessedTime();
lastModified = stats.getLastModifiedTime();
Object result = region.get(key0);
region.get(key1);
region.get(key2);
region.get(key3);
region.get(notPresentKey1);
region.get(notPresentKey2);
region.get(notPresentKey3);
region.get(notPresentKey4);
assertEquals(value, result);
assertEquals(2, stats.getMissCount());
assertEquals(2, stats.getHitCount());
assertTrue(stats.getLastAccessedTime() > lastAccessed);
});
vm0.invoke(() -> {
Region region = getRootRegion(name);
CacheStatistics stats = region.getStatistics();
lastAccessed = stats.getLastAccessedTime();
Object result = region.get(key0);
region.get(key1);
region.get(key2);
region.get(key3);
region.get(notPresentKey1);
region.get(notPresentKey2);
region.get(notPresentKey3);
region.get(notPresentKey4);
assertEquals(value, result);
assertEquals(4, stats.getHitCount());
assertEquals(4, stats.getMissCount());
assertTrue(stats.getLastAccessedTime() > lastAccessed);
});
vm0.invoke(() -> {
Region region = getRootRegion(name);
CacheStatistics stats = region.getStatistics();
long before = ((InternalRegion) region).cacheTimeMillis();
region.put(key0, value);
region.put(key1, value1);
region.put(key2, value2);
region.put(key3, value3);
long after = ((InternalRegion) region).cacheTimeMillis();
assertTrue(before <= stats.getLastModifiedTime());
assertTrue(after >= stats.getLastModifiedTime());
});
vm1.invoke(() -> {
Region region = getRootRegion(name);
CacheStatistics stats = region.getStatistics();
long before = ((InternalRegion) region).cacheTimeMillis();
region.put(key0, value);
region.put(key1, value1);
region.put(key2, value2);
region.put(key3, value3);
long after = ((InternalRegion) region).cacheTimeMillis();
assertTrue(before <= stats.getLastModifiedTime());
assertTrue(after >= stats.getLastModifiedTime());
});
vm0.invoke(() -> {
Region region = getRootRegion(name);
CacheStatistics stats = region.getStatistics();
lastAccessed = stats.getLastAccessedTime();
lastModified = stats.getLastModifiedTime();
region.invalidate(key0);
region.invalidate(key1);
assertEquals(lastAccessed, stats.getLastAccessedTime());
assertEquals(lastModified, stats.getLastModifiedTime());
assertEquals(4, stats.getHitCount());
assertEquals(4, stats.getMissCount());
});
vm1.invoke(() -> {
Region region = getRootRegion(name);
CacheStatistics stats = region.getStatistics();
lastAccessed = stats.getLastAccessedTime();
lastModified = stats.getLastModifiedTime();
region.invalidate(key2);
region.invalidate(key3);
assertEquals(lastAccessed, stats.getLastAccessedTime());
assertEquals(lastModified, stats.getLastModifiedTime());
assertEquals(4, stats.getHitCount());
assertEquals(4, stats.getMissCount());
});
vm0.invoke(() -> {
Region region = getRootRegion(name);
CacheStatistics stats = region.getStatistics();
lastModified = stats.getLastModifiedTime();
region.destroy(key0);
region.destroy(key1);
assertEquals(lastModified, stats.getLastModifiedTime());
assertEquals(4, stats.getHitCount());
assertEquals(4, stats.getMissCount());
});
vm1.invoke(() -> {
Region region = getRootRegion(name);
CacheStatistics stats = region.getStatistics();
lastModified = stats.getLastModifiedTime();
region.destroy(key2);
region.destroy(key3);
assertEquals(lastModified, stats.getLastModifiedTime());
assertEquals(4, stats.getHitCount());
assertEquals(4, stats.getMissCount());
});
}
/**
* Tests that an attempt to get statistics when they are disabled results in a
* {@link StatisticsDisabledException}.
*/
@Test
public void testDisabledStatistics() throws CacheException {
String name = getUniqueName();
Object key = "KEY";
Object value = "VALUE";
AttributesFactory factory = new AttributesFactory();
factory.setStatisticsEnabled(false);
Region region = createPartitionedRegion(name, factory.create());
assertThatThrownBy(region::getStatistics)
.isInstanceOf(StatisticsDisabledException.class);
region.put(key, value);
Region.Entry entry = region.getEntry(key);
assertThatThrownBy(entry::getStatistics).isInstanceOf(StatisticsDisabledException.class);
}
/**
* Tests that the stats values for a Proxy PartitionedRegion are all 0.
*/
@Test
public void testProxyRegionStats() throws CacheException {
String name = getUniqueName();
Object key = 0;
Object missingKey = 999;
Object value = "VALUE";
// Create the PARTITION_PROXY region
AttributesFactory proxyRegionAttrsFactory = new AttributesFactory();
proxyRegionAttrsFactory.setStatisticsEnabled(true);
PartitionAttributesFactory paf = new PartitionAttributesFactory();
// Setting LocalMaxMemory to 0 makes the region of PROXY type
paf.setLocalMaxMemory(0);
proxyRegionAttrsFactory.setPartitionAttributes(paf.create());
Region region = createPartitionedRegion(name, proxyRegionAttrsFactory.create());
// Create the region as not proxy on another server to be able to add entries.
VM vm0 = VM.getVM(0);
vm0.invoke(() -> {
AttributesFactory factory = new AttributesFactory();
factory.setStatisticsEnabled(true);
createPartitionedRegion(name, factory.create());
});
region.put(key, value);
region.get(key);
region.get(missingKey);
CacheStatistics stats = region.getStatistics();
assertEquals(0, stats.getLastModifiedTime());
assertEquals(0, stats.getLastAccessedTime());
assertEquals(0, stats.getHitCount());
assertEquals(0, stats.getMissCount());
assertEquals(0.0f, stats.getHitRatio(), 0.0f);
// Call reset counts to check that no exception is raised
stats.resetCounts();
}
}