Skip to content

Commit

Permalink
NeoStoreUtil access doesn't read trailer as record by mistake
Browse files Browse the repository at this point in the history
  • Loading branch information
tinwelint committed Jan 27, 2015
1 parent 54cbfe6 commit 6c82f3d
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 2 deletions.
Expand Up @@ -32,10 +32,14 @@
import org.neo4j.kernel.impl.store.NeoStore.Position;
import org.neo4j.kernel.impl.store.StoreId;
import org.neo4j.kernel.impl.storemigration.StoreFileType;
import org.neo4j.kernel.impl.storemigration.StoreVersionCheck;
import org.neo4j.kernel.impl.storemigration.StoreVersionCheck.Result;

import static java.lang.String.format;

import static org.neo4j.helpers.UTF8.encode;
import static org.neo4j.kernel.impl.store.NeoStore.RECORD_SIZE;
import static org.neo4j.kernel.impl.store.NeoStore.TYPE_DESCRIPTOR;

/**
* Reads the contents of a {@link NeoStore neostore} store. Namely all of its {@link Position records}
Expand Down Expand Up @@ -67,9 +71,19 @@ public NeoStoreUtil( File storeDir )

public NeoStoreUtil( File storeDir, FileSystemAbstraction fs )
{
try ( StoreChannel channel = fs.open( neoStoreFile( storeDir, StoreFileType.STORE ), "r" ) )
File neoStoreFile = neoStoreFile( storeDir, StoreFileType.STORE );
String currentTypeDescriptorAndVersion = NeoStore.buildTypeDescriptorAndVersion( TYPE_DESCRIPTOR );
boolean storeHasTrailer = hasTrailer( neoStoreFile, fs, currentTypeDescriptorAndVersion );
try ( StoreChannel channel = fs.open( neoStoreFile, "r" ) )
{
ByteBuffer buf = ByteBuffer.allocate( Position.values().length * RECORD_SIZE );
int contentSize = (int) channel.size();
if ( storeHasTrailer )
{
int trailerSize = encode( currentTypeDescriptorAndVersion ).length;
contentSize -= trailerSize;
}
int records = contentSize/RECORD_SIZE;
ByteBuffer buf = ByteBuffer.allocate( records * RECORD_SIZE );
channel.read( buf );
buf.flip();

Expand All @@ -84,6 +98,13 @@ public NeoStoreUtil( File storeDir, FileSystemAbstraction fs )
}
}

private boolean hasTrailer( File neoStoreFile, FileSystemAbstraction fs, String currentTypeDescriptorAndVersion )
{
StoreVersionCheck trailerCheck = new StoreVersionCheck( fs );
Result result = trailerCheck.hasVersion( neoStoreFile, currentTypeDescriptorAndVersion );
return result.outcome == Result.Outcome.ok || result.outcome == Result.Outcome.unexpectedUpgradingStoreVersion;
}

private long nextRecord( ByteBuffer buf )
{
buf.get(); // in use byte
Expand Down
@@ -0,0 +1,119 @@
/**
* Copyright (c) 2002-2015 "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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.store.record;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;

import org.junit.Rule;
import org.junit.Test;

import org.neo4j.io.fs.FileSystemAbstraction;
import org.neo4j.io.fs.StoreChannel;
import org.neo4j.kernel.impl.store.NeoStore;
import org.neo4j.kernel.impl.store.NeoStore.Position;
import org.neo4j.test.EphemeralFileSystemRule;

import static java.nio.ByteBuffer.wrap;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import static org.neo4j.helpers.UTF8.encode;
import static org.neo4j.kernel.impl.store.NeoStore.TYPE_DESCRIPTOR;

public class NeoStoreUtilTest
{
@Test
public void shouldNotReadTrailerAsRecordData() throws Exception
{
// GIVEN
long[] values = new long[] {1, 2, 3, 4, 5};
NeoStoreUtil access = new NeoStoreUtil( neoStoreFile( fs.get(), true, values ), fs.get() );

// WHEN
for ( int i = 0; i < values.length; i++ )
{
assertEquals( values[i], access.getValue( Position.values()[i] ) );
}

// THEN
try
{
access.getValue( Position.values()[values.length] );
fail( "Shouldn't have read more records" );
}
catch ( IllegalStateException e )
{ // OK
}
}

@Test
public void shouldReadAllRecordsEvenOnStoreWithoutTrailer() throws Exception
{
// GIVEN
long[] values = new long[] {1, 2, 3, 4, 5};
NeoStoreUtil access = new NeoStoreUtil( neoStoreFile( fs.get(), false, values ), fs.get() );

// WHEN
for ( int i = 0; i < values.length; i++ )
{
assertEquals( values[i], access.getValue( Position.values()[i] ) );
}

// THEN
try
{
access.getValue( Position.values()[values.length] );
fail( "Shouldn't have read more records" );
}
catch ( IllegalStateException e )
{ // OK
}
}

private File neoStoreFile( FileSystemAbstraction fs, boolean addVersionTrailer, long... recordValues )
throws IOException
{
File dir = new File( "dir" );
fs.mkdirs( dir );
try ( StoreChannel channel = fs.create( new File( dir, NeoStore.DEFAULT_NAME ) ) )
{
ByteBuffer buffer = ByteBuffer.allocate( NeoStore.RECORD_SIZE );
for ( long recordValue : recordValues )
{
buffer.clear();
buffer.put( Record.IN_USE.byteValue() );
buffer.putLong( recordValue );
buffer.flip();
channel.write( buffer );
}

if ( addVersionTrailer )
{
channel.write( wrap( encode( NeoStore.buildTypeDescriptorAndVersion( TYPE_DESCRIPTOR ) ) ) );
}
}
return dir;
}

public final @Rule EphemeralFileSystemRule fs = new EphemeralFileSystemRule();
}
Expand Up @@ -86,6 +86,9 @@ public static void changeVersionNumber( FileSystemAbstraction fileSystem, File s
fileChannel.close();
}

/**
* Removes the version trailer from the store files.
*/
public static void truncateFile( FileSystemAbstraction fileSystem, File storeFile,
String suffixToDetermineTruncationLength ) throws IOException
{
Expand Down

0 comments on commit 6c82f3d

Please sign in to comment.