Skip to content

Commit

Permalink
Add check for neo store commands
Browse files Browse the repository at this point in the history
  • Loading branch information
davidegrohmann committed Dec 22, 2015
1 parent a7540da commit c11f63f
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 13 deletions.
1 change: 0 additions & 1 deletion tools/src/main/java/org/neo4j/tools/txlog/CheckTxLogs.java
Expand Up @@ -92,7 +92,6 @@ private <C extends Command, R extends Abstract64BitRecord> void scan(
File[] logs, InconsistenciesHandler handler, CheckType<C,R> check ) throws IOException File[] logs, InconsistenciesHandler handler, CheckType<C,R> check ) throws IOException
{ {
System.out.println( "Checking logs for " + check.name() + " inconsistencies" ); System.out.println( "Checking logs for " + check.name() + " inconsistencies" );

CommittedRecords<R> state = new CommittedRecords<>( check ); CommittedRecords<R> state = new CommittedRecords<>( check );


for ( File log : logs ) for ( File log : logs )
Expand Down
19 changes: 11 additions & 8 deletions tools/src/main/java/org/neo4j/tools/txlog/CommittedRecords.java
Expand Up @@ -19,8 +19,9 @@
*/ */
package org.neo4j.tools.txlog; package org.neo4j.tools.txlog;


import org.neo4j.collection.primitive.Primitive; import java.util.HashMap;
import org.neo4j.collection.primitive.PrimitiveLongObjectMap; import java.util.Map;

import org.neo4j.kernel.impl.store.record.Abstract64BitRecord; import org.neo4j.kernel.impl.store.record.Abstract64BitRecord;
import org.neo4j.kernel.impl.store.record.NodeRecord; import org.neo4j.kernel.impl.store.record.NodeRecord;
import org.neo4j.kernel.impl.store.record.PropertyRecord; import org.neo4j.kernel.impl.store.record.PropertyRecord;
Expand All @@ -37,33 +38,35 @@
class CommittedRecords<R extends Abstract64BitRecord> class CommittedRecords<R extends Abstract64BitRecord>
{ {
private final CheckType<?,R> checkType; private final CheckType<?,R> checkType;
private final PrimitiveLongObjectMap<LogRecord<R>> recordsById; private final Map<Long,LogRecord<R>> recordsById;


CommittedRecords( CheckType<?,R> check ) CommittedRecords( CheckType<?,R> check )
{ {
this.checkType = check; this.checkType = check;
this.recordsById = Primitive.longObjectMap(); this.recordsById = new HashMap<>();
} }


boolean isValid( R record ) public boolean isValid( R record )
{ {
LogRecord<R> current = recordsById.get( record.getId() ); LogRecord<R> current = recordsById.get( record.getId() );
return current == null || checkType.equal( record, current.record() ); return current == null || checkType.equal( record, current.record() );
} }


void put( R record, long logVersion ) public void put( R record, long logVersion )
{ {
recordsById.put( record.getId(), new LogRecord<>( record, logVersion ) ); recordsById.put( record.getId(), new LogRecord<>( record, logVersion ) );
} }


LogRecord<R> get( long id ) public LogRecord<R> get( long id )
{ {
return recordsById.get( id ); return recordsById.get( id );
} }


@Override @Override
public String toString() public String toString()
{ {
return "CommittedRecords{" + "command=" + checkType.name() + ", recordsById.size=" + recordsById.size() + "}"; return "CommittedRecords{" +
"command=" + checkType.name() +
", recordsById.size=" + recordsById.size() + "}";
} }
} }
6 changes: 3 additions & 3 deletions tools/src/main/java/org/neo4j/tools/txlog/LogRecord.java
Expand Up @@ -29,18 +29,18 @@
* *
* @param <R> the type of the record * @param <R> the type of the record
*/ */
class LogRecord<R extends Abstract64BitRecord> public class LogRecord<R extends Abstract64BitRecord>
{ {
private final R record; private final R record;
private final long logVersion; private final long logVersion;


LogRecord( R record, long logVersion ) public LogRecord( R record, long logVersion )
{ {
this.record = record; this.record = record;
this.logVersion = logVersion; this.logVersion = logVersion;
} }


R record() public R record()
{ {
return record; return record;
} }
Expand Down
Expand Up @@ -28,10 +28,11 @@ public class CheckTypes
public static final PropertyCheckType PROPERTY = new PropertyCheckType(); public static final PropertyCheckType PROPERTY = new PropertyCheckType();
public static final RelationshipCheckType RELATIONSHIP = new RelationshipCheckType(); public static final RelationshipCheckType RELATIONSHIP = new RelationshipCheckType();
public static final RelationshipGroupCheckType RELATIONSHIP_GROUP = new RelationshipGroupCheckType(); public static final RelationshipGroupCheckType RELATIONSHIP_GROUP = new RelationshipGroupCheckType();
public static final NeoStoreCheckType NEO_STORE = new NeoStoreCheckType();


@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" )
public static final CheckType<? extends Command, ? extends Abstract64BitRecord>[] CHECK_TYPES = public static final CheckType<? extends Command, ? extends Abstract64BitRecord>[] CHECK_TYPES =
new CheckType[]{NODE, PROPERTY, RELATIONSHIP, RELATIONSHIP_GROUP}; new CheckType[]{NODE, PROPERTY, RELATIONSHIP, RELATIONSHIP_GROUP, NEO_STORE};


public static <C extends Command,R extends Abstract64BitRecord> CheckType<C,R> fromName( String name ) public static <C extends Command,R extends Abstract64BitRecord> CheckType<C,R> fromName( String name )
{ {
Expand Down
@@ -0,0 +1,60 @@
/*
* 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 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.tools.txlog.checktypes;

import java.util.Objects;

import org.neo4j.kernel.impl.store.record.NeoStoreRecord;
import org.neo4j.kernel.impl.transaction.command.Command;

public class NeoStoreCheckType extends CheckType<Command.NeoStoreCommand,NeoStoreRecord>
{
NeoStoreCheckType()
{
super( Command.NeoStoreCommand.class );
}

@Override
public NeoStoreRecord before( Command.NeoStoreCommand command )
{
return command.getBefore();
}

@Override
public NeoStoreRecord after( Command.NeoStoreCommand command )
{
return command.getAfter();
}

@Override
public boolean equal( NeoStoreRecord record1, NeoStoreRecord record2 )
{
Objects.requireNonNull( record1 );
Objects.requireNonNull( record2 );

return record1.getNextProp() == record2.getNextProp();
}

@Override
public String name()
{
return "neo_store";
}
}
112 changes: 112 additions & 0 deletions tools/src/test/java/org/neo4j/tools/txlog/CheckTxLogsTest.java
Expand Up @@ -32,6 +32,7 @@
import org.neo4j.io.fs.StoreChannel; import org.neo4j.io.fs.StoreChannel;
import org.neo4j.kernel.impl.store.PropertyStore; import org.neo4j.kernel.impl.store.PropertyStore;
import org.neo4j.kernel.impl.store.PropertyType; import org.neo4j.kernel.impl.store.PropertyType;
import org.neo4j.kernel.impl.store.record.NeoStoreRecord;
import org.neo4j.kernel.impl.store.record.NodeRecord; import org.neo4j.kernel.impl.store.record.NodeRecord;
import org.neo4j.kernel.impl.store.record.PropertyBlock; import org.neo4j.kernel.impl.store.record.PropertyBlock;
import org.neo4j.kernel.impl.store.record.PropertyRecord; import org.neo4j.kernel.impl.store.record.PropertyRecord;
Expand All @@ -54,6 +55,7 @@
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.neo4j.tools.txlog.checktypes.CheckTypes.NEO_STORE;
import static org.neo4j.tools.txlog.checktypes.CheckTypes.NODE; import static org.neo4j.tools.txlog.checktypes.CheckTypes.NODE;
import static org.neo4j.tools.txlog.checktypes.CheckTypes.PROPERTY; import static org.neo4j.tools.txlog.checktypes.CheckTypes.PROPERTY;
import static org.neo4j.tools.txlog.checktypes.CheckTypes.RELATIONSHIP; import static org.neo4j.tools.txlog.checktypes.CheckTypes.RELATIONSHIP;
Expand Down Expand Up @@ -535,6 +537,116 @@ public void shouldReportRelationshipGroupInconsistenciesFromDifferentLogs() thro
assertFalse(currentRecord2.inUse() ); assertFalse(currentRecord2.inUse() );
} }


@Test
public void shouldReportNeoStoreInconsistenciesFromSingleLog() throws IOException
{
// Given
File log = logFile( 1 );

writeTxContent( log,
new Command.NeoStoreCommand().init(
new NeoStoreRecord(),
createNeoStoreRecord( 42 )
),
new Command.PropertyCommand().init(
propertyRecord( 5, false, -1, -1 ),
propertyRecord( 5, true, -1, -1, 777 )
),
new Command.NeoStoreCommand().init(
createNeoStoreRecord( 42 ),
createNeoStoreRecord( 21 )
)
);

writeTxContent( log,
new Command.NeoStoreCommand().init(
createNeoStoreRecord( 42 ),
createNeoStoreRecord( 33 )
)
);

CapturingInconsistenciesHandler handler = new CapturingInconsistenciesHandler();
CheckTxLogs checker = new CheckTxLogs( fsRule.get() );

// When
checker.scan( new File[]{log}, handler, NEO_STORE );

// Then
assertEquals( 1, handler.inconsistencies.size() );

NeoStoreRecord seenRecord = (NeoStoreRecord) handler.inconsistencies.get( 0 ).committed.record();
NeoStoreRecord currentRecord = (NeoStoreRecord) handler.inconsistencies.get( 0 ).current.record();

assertEquals( 21, seenRecord.getNextProp() );
assertEquals( 42, currentRecord.getNextProp() );
}

@Test
public void shouldReportNeoStoreInconsistenciesFromDifferentLogs() throws IOException
{
// Given
File log1 = logFile( 1 );
File log2 = logFile( 2 );
File log3 = logFile( 3 );

writeTxContent( log1,
new Command.NeoStoreCommand().init(
new NeoStoreRecord(),
createNeoStoreRecord( 42 )
),
new Command.PropertyCommand().init(
propertyRecord( 5, false, -1, -1 ),
propertyRecord( 5, true, -1, -1, 777 )
),
new Command.NeoStoreCommand().init(
createNeoStoreRecord( 42 ),
createNeoStoreRecord( 21 )
)
);

writeTxContent( log2,
new Command.NeoStoreCommand().init(
createNeoStoreRecord( 12 ),
createNeoStoreRecord( 21 )
)
);

writeTxContent( log3,
new Command.NeoStoreCommand().init(
createNeoStoreRecord( 13 ),
createNeoStoreRecord( 21 )
)
);

CapturingInconsistenciesHandler handler = new CapturingInconsistenciesHandler();
CheckTxLogs checker = new CheckTxLogs( fsRule.get() );

// When
checker.scan( new File[]{log1, log2, log3}, handler, NEO_STORE );

// Then
assertEquals( 2, handler.inconsistencies.size() );

NeoStoreRecord seenRecord1 = (NeoStoreRecord) handler.inconsistencies.get( 0 ).committed.record();
NeoStoreRecord currentRecord1 = (NeoStoreRecord) handler.inconsistencies.get( 0 ).current.record();

assertEquals( 21, seenRecord1.getNextProp() );
assertEquals( 12, currentRecord1.getNextProp() );

NeoStoreRecord seenRecord2 = (NeoStoreRecord) handler.inconsistencies.get( 1 ).committed.record();
NeoStoreRecord currentRecord2 = (NeoStoreRecord) handler.inconsistencies.get( 1 ).current.record();

assertEquals( 21, seenRecord2.getNextProp() );
assertEquals( 13, currentRecord2.getNextProp() );
}

private NeoStoreRecord createNeoStoreRecord( int nextProp )
{
NeoStoreRecord neoStoreRecord = new NeoStoreRecord();
neoStoreRecord.setNextProp( nextProp );
return neoStoreRecord;
}

private static File logFile( long version ) private static File logFile( long version )
{ {
return new File( PhysicalLogFile.DEFAULT_NAME + PhysicalLogFile.DEFAULT_VERSION_SUFFIX + version ); return new File( PhysicalLogFile.DEFAULT_NAME + PhysicalLogFile.DEFAULT_VERSION_SUFFIX + version );
Expand Down

0 comments on commit c11f63f

Please sign in to comment.