/*
* Copyright (c) 2002-2009 "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.kernel.impl.nioneo.store;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.neo4j.kernel.impl.AbstractNeo4jTestCase;
public class TestIdGenerator
{
private String path()
{
String path = AbstractNeo4jTestCase.getStorePath( "xatest" );
new File( path ).mkdirs();
return path;
}
private String file( String name )
{
return path() + File.separator + name;
}
private String idGeneratorFile()
{
return file( "testIdGenerator.id" );
}
@Test
public void testCreateIdGenerator() throws IOException
{
try
{
IdGeneratorImpl.createGenerator( null );
fail( "Null filename should throw exception" );
}
catch ( IllegalArgumentException e )
{
} // good
try
{
IdGeneratorImpl.createGenerator( idGeneratorFile() );
new IdGeneratorImpl( idGeneratorFile(), 0 ).close();
fail( "Zero grab size should throw exception" );
}
catch ( IllegalArgumentException e )
{
} // good
try
{
new IdGeneratorImpl( "testIdGenerator.id", -1 ).close();
fail( "Negative grab size should throw exception" );
}
catch ( IllegalArgumentException e )
{
} // good
try
{
IdGenerator idGenerator = new IdGeneratorImpl( idGeneratorFile(),
1008 );
try
{
IdGeneratorImpl.createGenerator( idGeneratorFile() );
fail( "Creating a id generator with existing file name "
+ "should throw exception" );
}
catch ( IllegalStateException e )
{
} // good
idGenerator.close();
// verify that id generator is ok
FileChannel fileChannel = new FileInputStream(
idGeneratorFile() ).getChannel();
ByteBuffer buffer = ByteBuffer.allocate( 9 );
assertEquals( 9, fileChannel.read( buffer ) );
buffer.flip();
assertEquals( (byte) 0, buffer.get() );
assertEquals( 0l, buffer.getLong() );
buffer.flip();
int readCount = fileChannel.read( buffer );
if ( readCount != -1 && readCount != 0 )
{
fail( "Id generator header not ok read 9 + " + readCount
+ " bytes from file" );
}
fileChannel.close();
}
finally
{
File file = new File( idGeneratorFile() );
if ( file.exists() )
{
assertTrue( file.delete() );
}
}
}
@Test
public void testStickyGenerator()
{
try
{
IdGeneratorImpl.createGenerator( idGeneratorFile() );
IdGenerator idGen = new IdGeneratorImpl( idGeneratorFile(), 3 );
try
{
new IdGeneratorImpl( idGeneratorFile(), 3 );
fail( "Opening sticky id generator should throw exception" );
}
catch ( StoreFailureException e )
{ // good
}
idGen.close();
}
finally
{
File file = new File( idGeneratorFile() );
if ( file.exists() )
{
assertTrue( file.delete() );
}
}
}
@Test
public void testNextId()
{
try
{
IdGeneratorImpl.createGenerator( idGeneratorFile() );
IdGenerator idGenerator = new IdGeneratorImpl( idGeneratorFile(), 3 );
for ( long i = 0; i < 7; i++ )
{
assertEquals( i, idGenerator.nextId() );
}
idGenerator.freeId( 1 );
idGenerator.freeId( 3 );
idGenerator.freeId( 5 );
assertEquals( 7l, idGenerator.nextId() );
idGenerator.freeId( 6 );
idGenerator.close();
idGenerator = new IdGeneratorImpl( idGeneratorFile(), 5 );
idGenerator.freeId( 2 );
idGenerator.freeId( 4 );
assertEquals( 1l, idGenerator.nextId() );
idGenerator.freeId( 1 );
assertEquals( 3l, idGenerator.nextId() );
idGenerator.freeId( 3 );
assertEquals( 5l, idGenerator.nextId() );
idGenerator.freeId( 5 );
assertEquals( 6l, idGenerator.nextId() );
idGenerator.freeId( 6 );
assertEquals( 8l, idGenerator.nextId() );
idGenerator.freeId( 8 );
assertEquals( 9l, idGenerator.nextId() );
idGenerator.freeId( 9 );
idGenerator.close();
idGenerator = new IdGeneratorImpl( idGeneratorFile(), 3 );
assertEquals( 2l, idGenerator.nextId() );
assertEquals( 4l, idGenerator.nextId() );
assertEquals( 1l, idGenerator.nextId() );
assertEquals( 3l, idGenerator.nextId() );
assertEquals( 5l, idGenerator.nextId() );
assertEquals( 6l, idGenerator.nextId() );
assertEquals( 8l, idGenerator.nextId() );
assertEquals( 9l, idGenerator.nextId() );
assertEquals( 10l, idGenerator.nextId() );
assertEquals( 11l, idGenerator.nextId() );
idGenerator.close();
}
finally
{
File file = new File( idGeneratorFile() );
if ( file.exists() )
{
assertTrue( file.delete() );
}
}
}
@Test
public void testFreeId()
{
try
{
IdGeneratorImpl.createGenerator( idGeneratorFile() );
IdGenerator idGenerator = new IdGeneratorImpl( idGeneratorFile(), 3 );
for ( long i = 0; i < 7; i++ )
{
assertEquals( i, idGenerator.nextId() );
}
try
{
idGenerator.freeId( -1 );
fail( "Negative id should throw exception" );
}
catch ( IllegalArgumentException e )
{ // good
}
try
{
idGenerator.freeId( 7 );
fail( "Greater id than ever returned should throw exception" );
}
catch ( IllegalArgumentException e )
{ // good
}
for ( int i = 0; i < 7; i++ )
{
idGenerator.freeId( i );
}
idGenerator.close();
idGenerator = new IdGeneratorImpl( idGeneratorFile(), 2 );
assertEquals( 0l, idGenerator.nextId() );
assertEquals( 1l, idGenerator.nextId() );
assertEquals( 2l, idGenerator.nextId() );
idGenerator.close();
idGenerator = new IdGeneratorImpl( idGeneratorFile(), 2 );
assertEquals( 4l, idGenerator.nextId() );
assertEquals( 5l, idGenerator.nextId() );
assertEquals( 6l, idGenerator.nextId() );
assertEquals( 3l, idGenerator.nextId() );
idGenerator.close();
}
finally
{
File file = new File( idGeneratorFile() );
if ( file.exists() )
{
assertTrue( file.delete() );
}
}
}
@Test
public void testClose()
{
try
{
IdGeneratorImpl.createGenerator( idGeneratorFile() );
IdGenerator idGenerator = new IdGeneratorImpl( idGeneratorFile(), 2 );
idGenerator.close();
try
{
idGenerator.nextId();
fail( "nextId after close should throw exception" );
}
catch ( IllegalStateException e )
{ // good
}
try
{
idGenerator.freeId( 0 );
fail( "freeId after close should throw exception" );
}
catch ( IllegalArgumentException e )
{ // good
}
idGenerator = new IdGeneratorImpl( idGeneratorFile(), 2 );
assertEquals( 0l, idGenerator.nextId() );
assertEquals( 1l, idGenerator.nextId() );
assertEquals( 2l, idGenerator.nextId() );
idGenerator.close();
try
{
idGenerator.nextId();
fail( "nextId after close should throw exception" );
}
catch ( IllegalStateException e )
{ // good
}
try
{
idGenerator.freeId( 0 );
fail( "freeId after close should throw exception" );
}
catch ( IllegalArgumentException e )
{ // good
}
}
finally
{
File file = new File( idGeneratorFile() );
if ( file.exists() )
{
assertTrue( file.delete() );
}
}
}
@Test
public void testOddAndEvenWorstCase()
{
int capacity = 1024 * 8 + 1;
try
{
IdGeneratorImpl.createGenerator( idGeneratorFile() );
IdGenerator idGenerator = new IdGeneratorImpl( idGeneratorFile(),
128 );
for ( int i = 0; i < capacity; i++ )
{
idGenerator.nextId();
}
Map<Long,Object> freedIds = new HashMap<Long,Object>();
for ( long i = 1; i < capacity; i += 2 )
{
idGenerator.freeId( i );
freedIds.put( i, this );
}
idGenerator.close();
idGenerator = new IdGeneratorImpl( idGeneratorFile(), 2000 );
long oldId = -1;
for ( int i = 0; i < capacity - 1; i += 2 )
{
long id = idGenerator.nextId();
if ( freedIds.remove( id ) == null )
{
throw new RuntimeException( "Id=" + id + " prevId=" + oldId
+ " list.size()=" + freedIds.size() );
}
oldId = id;
}
assertTrue( freedIds.values().size() == 0 );
idGenerator.close();
}
finally
{
File file = new File( idGeneratorFile() );
if ( file.exists() )
{
assertTrue( file.delete() );
}
}
try
{
IdGeneratorImpl.createGenerator( idGeneratorFile() );
IdGenerator idGenerator = new IdGeneratorImpl( idGeneratorFile(),
128 );
for ( int i = 0; i < capacity; i++ )
{
idGenerator.nextId();
}
Map<Long,Object> freedIds = new HashMap<Long,Object>();
for ( long i = 0; i < capacity; i += 2 )
{
idGenerator.freeId( i );
freedIds.put( i, this );
}
idGenerator.close();
idGenerator = new IdGeneratorImpl( idGeneratorFile(), 2000 );
for ( int i = 0; i < capacity; i += 2 )
{
assertEquals( this, freedIds.remove( idGenerator.nextId() ) );
}
assertEquals( 0, freedIds.values().size() );
idGenerator.close();
}
finally
{
File file = new File( idGeneratorFile() );
if ( file.exists() )
{
assertTrue( file.delete() );
}
}
}
@Test
public void testRandomTest()
{
int numberOfCloses = 0;
java.util.Random random = new java.util.Random( System
.currentTimeMillis() );
int capacity = random.nextInt( 1024 ) + 1024;
int grabSize = random.nextInt( 128 ) + 128;
IdGeneratorImpl.createGenerator( idGeneratorFile() );
IdGenerator idGenerator = new IdGeneratorImpl( idGeneratorFile(),
grabSize );
List<Long> idsTaken = new ArrayList<Long>();
float releaseIndex = 0.25f;
float closeIndex = 0.05f;
int currentIdCount = 0;
try
{
while ( currentIdCount < capacity )
{
float rIndex = random.nextFloat();
if ( rIndex < releaseIndex && currentIdCount > 0 )
{
idGenerator.freeId( idsTaken.remove(
random.nextInt( currentIdCount ) ).intValue() );
currentIdCount--;
}
else
{
idsTaken.add( idGenerator.nextId() );
currentIdCount++;
}
if ( rIndex > (1.0f - closeIndex) || rIndex < closeIndex )
{
idGenerator.close();
grabSize = random.nextInt( 128 ) + 128;
idGenerator = new IdGeneratorImpl( idGeneratorFile(),
grabSize );
numberOfCloses++;
}
}
idGenerator.close();
}
finally
{
File file = new File( idGeneratorFile() );
if ( file.exists() )
{
assertTrue( file.delete() );
}
}
}
@Test
public void testUnsignedId()
{
try
{
IdGeneratorImpl.createGenerator( idGeneratorFile() );
IdGenerator idGenerator = new IdGeneratorImpl( idGeneratorFile(), 1 );
idGenerator.setHighId( 4294967293l );
long id = idGenerator.nextId();
assertEquals( 4294967293l, id );
idGenerator.freeId( id );
try
{
idGenerator.nextId();
}
catch ( StoreFailureException e )
{ // good, capacity exceeded
}
idGenerator.close();
idGenerator = new IdGeneratorImpl( idGeneratorFile(), 1 );
assertEquals( 4294967294l, idGenerator.getHighId() );
id = idGenerator.nextId();
assertEquals( 4294967293l, id );
try
{
idGenerator.nextId();
}
catch ( StoreFailureException e )
{ // good, capacity exceeded
}
idGenerator.close();
}
finally
{
File file = new File( idGeneratorFile() );
if ( file.exists() )
{
assertTrue( file.delete() );
}
}
}
}