/**
* Copyright (c) 2002-2012 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.consistency.store.paging;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.matchers.JUnitMatchers.hasItem;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.junit.Test;
public class CartTest
{
@Test
public void linearScanWithSingleHitPerWindowLeadsToFifoEviction() throws Exception
{
// given
int capacity = 10;
StorageSpy storage = new StorageSpy( capacity * 2 );
Cart cart = new Cart( capacity );
// when
for ( int i = 0; i < capacity * 2; i++ )
{
cart.acquire( storage.page( i ), storage );
}
// then
assertArrayEquals( new String[]{
"L0", "L1", "L2", "L3", "L4", "L5", "L6", "L7", "L8", "L9",
"E0", "L10", "E1", "L11", "E2", "L12", "E3", "L13", "E4",
"L14", "E5", "L15", "E6", "L16", "E7", "L17", "E8", "L18", "E9", "L19"
}, storage.events.toArray() );
}
@Test
public void reverseLinearScanWithSingleHitPerWindowLeadsToFifoEviction() throws Exception
{
// given
int capacity = 10;
StorageSpy storage = new StorageSpy( capacity * 2 );
Cart cart = new Cart( capacity );
// when
for ( int i = capacity * 2 - 1; i >= 0; i-- )
{
cart.acquire( storage.page( i ), storage );
}
// then
assertArrayEquals( new String[]{
"L19", "L18", "L17", "L16", "L15", "L14", "L13", "L12", "L11", "L10",
"E19", "L9", "E18", "L8", "E17", "L7", "E16", "L6", "E15", "L5",
"E14", "L4", "E13", "L3", "E12", "L2", "E11", "L1", "E10", "L0",
}, storage.events.toArray() );
}
@Test
public void frequentlyAccessedPagesDoNotGetEvicted() throws Exception
{
// given
int capacity = 10;
StorageSpy storage = new StorageSpy( capacity * 2 );
Cart cart = new Cart( capacity );
// when
for ( int i = 0; i < capacity * 2; i++ )
{
// even number pages accessed more frequently
cart.acquire( storage.page( (i / 2) * 2 ), storage );
// background access of even and odd numbered pages
cart.acquire( storage.page( i ), storage );
}
// then
assertThat( storage.events, not( hasItem( "E0" ) ) );
assertThat( storage.events, hasItem( "E1" ) );
assertThat( storage.events, not( hasItem( "E2" ) ) );
assertThat( storage.events, hasItem( "E3" ) );
assertThat( storage.events, not( hasItem( "E4" ) ) );
assertThat( storage.events, hasItem( "E5" ) );
assertThat( storage.events, not( hasItem( "E6" ) ) );
assertThat( storage.events, hasItem( "E7" ) );
assertThat( storage.events, not( hasItem( "E8" ) ) );
assertThat( storage.events, hasItem( "E9" ) );
}
@Test
public void linearPlusRandom() throws Exception
{
// given
int capacity = 100;
StorageSpy storage = new StorageSpy( capacity * 2 );
int randoms = 4;
int pageSize = 10;
Cart cart = new Cart( capacity );
// when
Random random = new Random();
for ( int i = 0; i < capacity * 2; i++ )
{
// background access of even and odd numbered pages
for ( int j = 0; j < pageSize; j++ )
{
storage.linear = true;
cart.acquire( storage.page( i ), storage );
storage.linear = false;
for ( int k = 0; k < randoms; k++ )
{
cart.acquire( storage.page( random.nextInt( capacity * 2 ) ), storage );
}
}
}
// then
assertTrue( storage.linMiss * pageSize <= (storage.linMiss + storage.linHit) );
assertTrue( storage.rndMiss * 2 <= (storage.rndMiss + storage.rndHit) * 1.05 );
}
@Test
public void shouldReloadAfterForcedEviction() throws Exception
{
// given
int capacity = 10;
StorageSpy storage = new StorageSpy( capacity * 2 );
Cart cart = new Cart( capacity );
// when
cart.acquire( storage.page( 0 ), storage );
cart.forceEvict( storage.page( 0 ) );
cart.acquire( storage.page( 0 ), storage );
// then
assertArrayEquals( new String[]{
"L0", "E0", "L0"
}, storage.events.toArray() );
}
private static class StorageSpy implements PageReplacementStrategy.Storage<Integer, StorageSpy.SpyPage>
{
SpyPage[] pages;
StorageSpy( int pageCount )
{
pages = new SpyPage[pageCount];
for ( int i = 0; i < pages.length; i++ )
{
pages[i] = new SpyPage( i );
}
}
List<String> events = new ArrayList<String>();
int hitCount, loadCount;
boolean linear;
int linHit, linMiss;
int rndHit, rndMiss;
@Override
public Integer load( StorageSpy.SpyPage page )
{
events.add( "L" + page.address );
loadCount++;
if ( linear )
{
linMiss++;
}
else
{
rndMiss++;
}
return page.address;
}
public SpyPage page( int address )
{
return pages[address];
}
class SpyPage extends Page<Integer>
{
private final int address;
public SpyPage( int address )
{
this.address = address;
}
@Override
protected void evict( Integer address )
{
events.add( "E" + address );
}
@Override
protected void hit()
{
hitCount++;
if ( linear )
{
linHit++;
}
else
{
rndHit++;
}
}
}
}
}