blob: 6cb9a086d3a2eb32be5069b384fecec9830c34d3 [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.internal.cache.lru;
import java.util.Properties;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import com.gemstone.gemfire.StatisticDescriptor;
import com.gemstone.gemfire.StatisticsFactory;
import com.gemstone.gemfire.StatisticsType;
import com.gemstone.gemfire.StatisticsTypeFactory;
import com.gemstone.gemfire.SystemFailure;
import com.gemstone.gemfire.cache.AttributesFactory;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.CacheExistsException;
import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.cache.EvictionAction;
import com.gemstone.gemfire.cache.EvictionAlgorithm;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.distributed.DistributedSystem;
import com.gemstone.gemfire.internal.StatisticsTypeFactoryImpl;
import com.gemstone.gemfire.internal.cache.InternalRegionArguments;
import com.gemstone.gemfire.internal.cache.PlaceHolderDiskRegion;
import com.gemstone.gemfire.test.junit.categories.IntegrationTest;
/** This class tests the LRUCapacityController's core clock algorithm. */
@Category(IntegrationTest.class)
public class LRUClockJUnitTest extends junit.framework.TestCase {
private String myTestName;
//static int unusedPort = AvailablePort.getRandomAvailablePort(AvailablePort.JGROUPS);
static Properties sysProps = new Properties();
static {
//sysProps.setProperty("mcast-port", String.valueOf(unusedPort));
// a loner is all this test needs
sysProps.setProperty("mcast-port", "0");
sysProps.setProperty("locators", "");
}
public LRUClockJUnitTest( String name ) {
super( name );
this.myTestName = name;
}
protected LRUClockJUnitTest( String prefix, String methodName ) {
super( methodName );
this.myTestName = prefix + methodName;
}
/**
* The JUnit setup method
*
* @exception Exception Description of the Exception
*/
@Before
public void setUp() throws Exception {
super.setUp();
System.out.println( "\n\n### beginning " + this.myTestName + "###" );
}
/**
* The teardown method for JUnit
*
* @exception Exception Description of the Exception
*/
@After
public void tearDown() throws Exception {
super.tearDown();
System.out.println( "### finished " + this.myTestName + "###" );
}
/**
* A unit test for JUnit
*
* @exception Exception Description of the Exception
*/
@Test
public void testAddToClockFace() throws Exception {
NewLRUClockHand clock = getAClockHand( getARegion(), new TestEnableLRU() );
// getLRUEntry( maxScan )
LRUTestEntry[] nodes = new LRUTestEntry[10];
int i = 0;
for( i = 0; i < 10; i++ ) {
nodes[i] = getANode( i );
clock.appendEntry( nodes[i] );
}
// getLRUEntry until empty... verify order of results.
for( i = 0; i < 10; i++ ) {
LRUTestEntry n = (LRUTestEntry) clock.getLRUEntry( );
assertTrue( "expected nodes[" + nodes[i].id() + "], found nodes[" + n.id() + "]", n == nodes[i] );
}
assertTrue( "expected null", clock.getLRUEntry( ) == null );
}
@Test
public void testFIFO() {
try {
NewLRUClockHand clock = getAClockHand( getARegion(), new TestEnableLRU() );
for( int i = 0; i < 100; i++ ) {
LRUClockNode entry = getANode( i );
clock.appendEntry( entry );
}
for( int i = 100; i < 2000; i++ ) {
LRUClockNode entry = getANode( i );
clock.appendEntry( entry );
Object obj = clock.getLRUEntry();
if ( obj instanceof LRUTestEntry ) {
LRUTestEntry le = (LRUTestEntry) obj;
le.setEvicted();
} else {
assertTrue( "found wrong type: " + obj.getClass().getName(), false );
}
}
int counter = 0;
LRUTestEntry le = (LRUTestEntry) clock.getLRUEntry();
while( le != null ) {
counter++;
le = (LRUTestEntry) clock.getLRUEntry();
}
assertTrue( "expected 100, found " + counter, counter == 100);
}
catch (VirtualMachineError e) {
SystemFailure.initiateFailure(e);
throw e;
}
catch ( Throwable t ) {
t.printStackTrace();
assertTrue( "failed", false );
}
}
@Test
public void testEvicted() throws Exception {
NewLRUClockHand clock = getAClockHand( getARegion(), new TestEnableLRU() );
// getLRUEntry( maxScan )
LRUTestEntry[] nodes = new LRUTestEntry[10];
int i = 0;
for( i = 0; i < 10; i++ ) {
nodes[i] = getANode( i );
clock.appendEntry( nodes[i] );
}
for( i = 0; i < 10; i += 2 ) {
clock.unlinkEntry( nodes[i] );
}
// getLRUEntry until empty... verify order of results.
for( i = 1; i < 10; i += 2 ) {
LRUTestEntry n = (LRUTestEntry) clock.getLRUEntry( );
assertTrue( "expected nodes[" + nodes[i].id() + "], found nodes[" + n.id() + "]", n == nodes[i] );
}
assertTrue( "expected null", clock.getLRUEntry( ) == null );
}
@Test
public void testRecentlyUsed() throws Exception {
NewLRUClockHand clock = getAClockHand( getARegion(), new TestEnableLRU() );
// getLRUEntry( maxScan )
LRUTestEntry[] nodes = new LRUTestEntry[10];
int i = 0;
for( i = 0; i < 10; i++ ) {
nodes[i] = getANode( i );
clock.appendEntry( nodes[i] );
if ( i % 2 == 0 ) {
nodes[i].setRecentlyUsed();
}
}
// getLRUEntry until empty... verify order of results.
// should find 1, 3, etc... as 0, 2, 4 etc... were marked recently used..
for( i = 1; i < 10; i += 2 ) {
LRUTestEntry n = (LRUTestEntry) clock.getLRUEntry( );
assertTrue( "expected nodes[" + nodes[i].id() + "], found nodes[" + n.id() + "]", n == nodes[i] );
}
// now 0, 2, 4 should go...
for( i = 0; i < 10; i += 2 ) {
LRUTestEntry n = (LRUTestEntry) clock.getLRUEntry( );
assertTrue( "expected nodes[" + nodes[i].id() + "], found nodes[" + n.id() + "]", n == nodes[i] );
}
assertTrue( "expected null", clock.getLRUEntry( ) == null );
}
@Test
public void testRemoveHead() throws Exception {
NewLRUClockHand clock = getAClockHand( getARegion(), new TestEnableLRU() );
LRUTestEntry[] nodes = new LRUTestEntry[10];
int i = 0;
for( i = 0; i < 10; i++ ) {
nodes[i] = getANode( i );
clock.appendEntry( nodes[i] );
}
clock.unlinkEntry(nodes[0]);
for( i = 1; i < 10; i ++ ) {
LRUTestEntry n = (LRUTestEntry) clock.getLRUEntry( );
assertTrue( "expected nodes[" + nodes[i].id() + "], found nodes[" + n.id() + "]", n == nodes[i] );
}
assertEquals(null, clock.getLRUEntry( ));
}
@Test
public void testRemoveMiddle() throws Exception {
NewLRUClockHand clock = getAClockHand( getARegion(), new TestEnableLRU() );
LRUTestEntry[] nodes = new LRUTestEntry[10];
int i = 0;
for( i = 0; i < 10; i++ ) {
nodes[i] = getANode( i );
clock.appendEntry( nodes[i] );
}
clock.unlinkEntry(nodes[5]);
for( i = 0; i < 5; i ++ ) {
LRUTestEntry n = (LRUTestEntry) clock.getLRUEntry( );
assertTrue( "expected nodes[" + nodes[i].id() + "], found nodes[" + n.id() + "]", n == nodes[i] );
}
for( i = 6; i < 10; i ++ ) {
LRUTestEntry n = (LRUTestEntry) clock.getLRUEntry( );
assertTrue( "expected nodes[" + nodes[i].id() + "], found nodes[" + n.id() + "]", n == nodes[i] );
}
assertEquals(null, clock.getLRUEntry( ));
}
@Test
public void testRemoveTail() throws Exception {
NewLRUClockHand clock = getAClockHand( getARegion(), new TestEnableLRU() );
LRUTestEntry[] nodes = new LRUTestEntry[10];
int i = 0;
for( i = 0; i < 10; i++ ) {
nodes[i] = getANode( i );
clock.appendEntry( nodes[i] );
}
clock.unlinkEntry(nodes[9]);
for( i = 0; i < 9; i ++ ) {
LRUTestEntry n = (LRUTestEntry) clock.getLRUEntry( );
assertTrue( "expected nodes[" + nodes[i].id() + "], found nodes[" + n.id() + "]", n == nodes[i] );
}
assertEquals(null, clock.getLRUEntry( ));
}
/** manufacture a node so that a shared type can be used by SharedLRUClockTest. */
protected LRUTestEntry getANode( int id ) {
return new LocalLRUTestEntry( id );
}
public static interface LRUTestEntry extends LRUClockNode {
public int id();
}
/** test implementation of an LRUClockNode */
public static class LocalLRUTestEntry implements LRUTestEntry {
int id;
LRUClockNode next;
LRUClockNode prev;
int size;
boolean recentlyUsed;
boolean evicted;
public LocalLRUTestEntry( int id ) {
this.id = id;
next = null;
prev = null;
size = 0;
recentlyUsed = false;
evicted = false;
}
public int id() {
return id;
}
public boolean isTombstone() {
return false;
}
public void setNextLRUNode( LRUClockNode next ) {
this.next = next;
}
public LRUClockNode nextLRUNode() {
return this.next;
}
public void setPrevLRUNode( LRUClockNode prev ) {
this.prev = prev;
}
public LRUClockNode prevLRUNode() {
return this.prev;
}
public int updateEntrySize( EnableLRU cc ) {
return this.size = 1;
}
public int updateEntrySize(EnableLRU cc, Object value) {
return this.size = 1;
}
public int getEntrySize() {
return this.size;
}
/** this should only happen with the LRUClockHand sync'ed */
public void setEvicted() {
evicted = true;
}
public void unsetEvicted() {
evicted = false;
}
public boolean testEvicted( ) {
return evicted;
}
public boolean testRecentlyUsed() {
return recentlyUsed;
}
public void setRecentlyUsed() {
recentlyUsed = true;
}
public void unsetRecentlyUsed() {
recentlyUsed = false;
}
public LRUClockNode absoluteSelf( ) { return this; }
public LRUClockNode clearClones( ) { return this; }
public int cloneCount( ) { return 0; }
}
public class TestEnableLRU implements EnableLRU {
private final StatisticsType statType;
{
// create the stats type for MemLRU.
StatisticsTypeFactory f = StatisticsTypeFactoryImpl.singleton();
final String bytesAllowedDesc =
"Number of total bytes allowed in this region.";
final String byteCountDesc =
"Number of bytes in region.";
final String lruEvictionsDesc =
"Number of total entry evictions triggered by LRU.";
final String lruEvaluationsDesc =
"Number of entries evaluated during LRU operations.";
final String lruGreedyReturnsDesc =
"Number of non-LRU entries evicted during LRU operations";
final String lruDestroysDesc =
"Number of entry destroys triggered by LRU.";
final String lruDestroysLimitDesc =
"Maximum number of entry destroys triggered by LRU before scan occurs.";
statType = f.createType( "TestLRUStatistics",
"Statistics about byte based Least Recently Used region entry disposal",
new StatisticDescriptor[] {
f.createLongGauge("bytesAllowed", bytesAllowedDesc, "bytes" ),
f.createLongGauge("byteCount", byteCountDesc, "bytes" ),
f.createLongCounter("lruEvictions", lruEvictionsDesc, "entries" ),
f.createLongCounter("lruEvaluations", lruEvaluationsDesc, "entries" ),
f.createLongCounter("lruGreedyReturns", lruGreedyReturnsDesc, "entries"),
f.createLongCounter("lruDestroys", lruDestroysDesc, "entries" ),
f.createLongCounter("lruDestroysLimit", lruDestroysLimitDesc, "entries" ),
}
);
}
public int entrySize( Object key, Object value ) throws IllegalArgumentException {
return 1;
}
public long limit( ) {
return 20;
}
public boolean usesMem( ) {
return false;
}
public EvictionAlgorithm getEvictionAlgorithm() {
return EvictionAlgorithm.LRU_ENTRY;
}
public LRUStatistics getStats() {
return null;
}
public EvictionAction getEvictionAction() {
return EvictionAction.DEFAULT_EVICTION_ACTION;
}
public StatisticsType getStatisticsType() {
return statType;
}
public String getStatisticsName() {
return "TestLRUStatistics";
}
public int getLimitStatId() {
return statType.nameToId("bytesAllowed");
}
public int getCountStatId() {
return statType.nameToId("byteCount");
}
public int getEvictionsStatId() {
return statType.nameToId("lruEvictions");
}
public int getDestroysStatId() {
return statType.nameToId("lruDestroys");
}
public int getDestroysLimitStatId() {
return statType.nameToId("lruDestroysLimit");
}
public int getEvaluationsStatId() {
return statType.nameToId("lruEvaluations");
}
public int getGreedyReturnsStatId() {
return statType.nameToId("lruGreedyReturns");
}
public boolean mustEvict(LRUStatistics stats, Region region, int delta) {
throw new UnsupportedOperationException("Not implemented");
}
public void afterEviction() {
throw new UnsupportedOperationException("Not implemented");
}
public LRUStatistics initStats(Object region, StatisticsFactory sf)
{
String regionName;
if (region instanceof Region) {
regionName = ((Region)region).getName();
} else if (region instanceof PlaceHolderDiskRegion) {
regionName = ((PlaceHolderDiskRegion)region).getName();
// @todo make it shorter (I think it is the fullPath
} else {
throw new IllegalStateException("expected Region or PlaceHolderDiskRegion");
}
final LRUStatistics stats = new LRUStatistics(sf, "TestLRUStatistics" + regionName, this);
stats.setLimit( limit() );
return stats;
}
}
/** overridden in SharedLRUClockTest to test SharedLRUClockHand */
protected NewLRUClockHand getAClockHand( Region reg, EnableLRU elru ) {
return new NewLRUClockHand( reg, elru,new InternalRegionArguments());
}
private Region getARegion() throws Exception {
DistributedSystem ds = DistributedSystem.connect( sysProps );
Cache c = null;
try {
c = CacheFactory.create( ds );
} catch ( CacheExistsException cee ) {
c = CacheFactory.getInstance( ds );
}
AttributesFactory af = new AttributesFactory();
Region root = c.getRegion("root");
if ( root == null ) {
root = c.createRegion("root", af.create() );
}
Region sub = root.createSubregion( myTestName, af.create() );
return sub;
}
}