From 569e4d473307ce0d1957746e9a42613f82847841 Mon Sep 17 00:00:00 2001 From: Davide Grohmann Date: Wed, 9 Dec 2015 11:30:51 +0100 Subject: [PATCH] Small refactor to allow adding more check types --- .../org/neo4j/tools/txlog/CheckTxLogs.java | 36 ++-- .../java/org/neo4j/tools/txlog/CheckType.java | 195 ------------------ .../neo4j/tools/txlog/CommittedRecords.java | 1 + .../tools/txlog/checktypes/CheckType.java | 57 +++++ .../tools/txlog/checktypes/CheckTypes.java | 14 ++ .../tools/txlog/checktypes/NodeCheckType.java | 46 +++++ .../txlog/checktypes/PropertyCheckType.java | 97 +++++++++ .../neo4j/tools/txlog/CheckTxLogsTest.java | 10 +- 8 files changed, 240 insertions(+), 216 deletions(-) delete mode 100644 tools/src/main/java/org/neo4j/tools/txlog/CheckType.java create mode 100644 tools/src/main/java/org/neo4j/tools/txlog/checktypes/CheckType.java create mode 100644 tools/src/main/java/org/neo4j/tools/txlog/checktypes/CheckTypes.java create mode 100644 tools/src/main/java/org/neo4j/tools/txlog/checktypes/NodeCheckType.java create mode 100644 tools/src/main/java/org/neo4j/tools/txlog/checktypes/PropertyCheckType.java diff --git a/tools/src/main/java/org/neo4j/tools/txlog/CheckTxLogs.java b/tools/src/main/java/org/neo4j/tools/txlog/CheckTxLogs.java index 6c4ce3c731c7b..a19f51ccda470 100644 --- a/tools/src/main/java/org/neo4j/tools/txlog/CheckTxLogs.java +++ b/tools/src/main/java/org/neo4j/tools/txlog/CheckTxLogs.java @@ -22,7 +22,6 @@ import java.io.File; import java.io.IOException; import java.util.Arrays; -import java.util.Comparator; import org.neo4j.helpers.Args; import org.neo4j.io.fs.DefaultFileSystemAbstraction; @@ -35,18 +34,19 @@ import org.neo4j.kernel.impl.transaction.log.entry.LogEntry; import org.neo4j.kernel.impl.transaction.log.entry.LogEntryCommand; import org.neo4j.test.LogTestUtils; +import org.neo4j.tools.txlog.checktypes.CheckType; +import org.neo4j.tools.txlog.checktypes.CheckTypes; /** * Tool that verifies consistency of transaction logs. - *

+ * * Transaction log is considered consistent when every command's before state is the same as after state for * corresponding record in previously committed transaction. - *

+ * * Tool expects a single argument - directory with transaction logs. * It then simply iterates over all commands in those logs, compares before state for current record with previously * seen after state and stores after state for current record, if before state is consistent. */ -//: TODO introduce abstract tool class as soon as we will have several tools in tools module public class CheckTxLogs { private static final String HELP_FLAG = "help"; @@ -72,12 +72,19 @@ public static void main( String[] args ) throws Exception CheckTxLogs tool = new CheckTxLogs( new DefaultFileSystemAbstraction() ); - tool.scan( logs, CheckType.NODE, new PrintingInconsistenciesHandler() ); - tool.scan( logs, CheckType.PROPERTY, new PrintingInconsistenciesHandler() ); + tool.scan( logs, new PrintingInconsistenciesHandler(), CheckTypes.CHECK_TYPES ); } - void scan( File[] logs, CheckType check, - InconsistenciesHandler handler ) throws IOException + void scan( File[] logs, InconsistenciesHandler handler, CheckType... checkTypes ) throws IOException + { + for ( CheckType checkType : checkTypes ) + { + scan( logs, handler, checkType ); + } + } + + private void scan( + File[] logs, InconsistenciesHandler handler, CheckType check ) throws IOException { System.out.println( "Checking logs for " + check.name() + " inconsistencies" ); @@ -144,15 +151,10 @@ private static File parseDir( Args args ) private static File[] txLogsIn( File dir ) { File[] logs = dir.listFiles( LogFiles.FILENAME_FILTER ); - Arrays.sort( logs, new Comparator() - { - @Override - public int compare( File f1, File f2 ) - { - long f1Version = PhysicalLogFiles.getLogVersion( f1 ); - long f2Version = PhysicalLogFiles.getLogVersion( f2 ); - return Long.compare( f1Version, f2Version ); - } + Arrays.sort( logs, ( f1, f2 ) -> { + long f1Version = PhysicalLogFiles.getLogVersion( f1 ); + long f2Version = PhysicalLogFiles.getLogVersion( f2 ); + return Long.compare( f1Version, f2Version ); } ); return logs; } diff --git a/tools/src/main/java/org/neo4j/tools/txlog/CheckType.java b/tools/src/main/java/org/neo4j/tools/txlog/CheckType.java deleted file mode 100644 index 382ac591b34e5..0000000000000 --- a/tools/src/main/java/org/neo4j/tools/txlog/CheckType.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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 . - */ -package org.neo4j.tools.txlog; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -import org.neo4j.kernel.impl.store.record.Abstract64BitRecord; -import org.neo4j.kernel.impl.store.record.NodeRecord; -import org.neo4j.kernel.impl.store.record.PropertyBlock; -import org.neo4j.kernel.impl.store.record.PropertyRecord; -import org.neo4j.kernel.impl.transaction.command.Command; -import org.neo4j.kernel.impl.transaction.command.Command.NodeCommand; -import org.neo4j.kernel.impl.transaction.command.Command.PropertyCommand; - -/** - * Type of command ({@link NodeCommand}, {@link PropertyCommand}, ...) to check during transaction log verification. - * This class exists to mitigate the absence of interfaces for commands with before and after state. - * It also provides an alternative equality check instead of {@link Abstract64BitRecord#equals(Object)} that only - * checks {@linkplain Abstract64BitRecord#getLongId() entity id}. - * - * @param the type of command to check - * @param the type of records that this command contains - */ -abstract class CheckType -{ - public static final NodeCheckType NODE = new NodeCheckType(); - public static final PropertyCheckType PROPERTY = new PropertyCheckType(); - - private final Class recordClass; - - CheckType( Class recordClass ) - { - this.recordClass = recordClass; - } - - Class commandClass() - { - return recordClass; - } - - abstract R before( C command ); - - abstract R after( C command ); - - abstract boolean equal( R record1, R record2 ); - - abstract String name(); - - private static class NodeCheckType extends CheckType - { - NodeCheckType() - { - super( NodeCommand.class ); - } - - @Override - NodeRecord before( NodeCommand command ) - { - return command.getBefore(); - } - - @Override - NodeRecord after( NodeCommand command ) - { - return command.getAfter(); - } - - @Override - boolean equal( NodeRecord record1, NodeRecord record2 ) - { - Objects.requireNonNull( record1 ); - Objects.requireNonNull( record2 ); - - return record1.getId() == record2.getId() && - record1.inUse() == record2.inUse() && - record1.getNextProp() == record2.getNextProp() && - record1.getNextRel() == record2.getNextRel() && - record1.isDense() == record2.isDense() && - record1.getLabelField() == record2.getLabelField(); - } - - @Override - String name() - { - return "node"; - } - } - - private static class PropertyCheckType extends CheckType - { - PropertyCheckType() - { - super( PropertyCommand.class ); - } - - @Override - PropertyRecord before( PropertyCommand command ) - { - return command.getBefore(); - } - - @Override - PropertyRecord after( PropertyCommand command ) - { - return command.getAfter(); - } - - @Override - boolean equal( PropertyRecord record1, PropertyRecord record2 ) - { - Objects.requireNonNull( record1 ); - Objects.requireNonNull( record2 ); - - if ( record1.getId() != record2.getId() ) - { - return false; - } - if ( record1.inUse() != record2.inUse() ) - { - return false; - } - if ( !record1.inUse() ) - { - return true; - } - return record1.isNodeSet() == record2.isNodeSet() && - record1.isRelSet() == record2.isRelSet() && - record1.getNodeId() == record2.getNodeId() && - record1.getRelId() == record2.getRelId() && - record1.getNextProp() == record2.getNextProp() && - record1.getPrevProp() == record2.getPrevProp() && - blocksEqual( record1, record2 ); - } - - @Override - String name() - { - return "property"; - } - - static boolean blocksEqual( PropertyRecord r1, PropertyRecord r2 ) - { - if ( r1.size() != r2.size() ) - { - return false; - } - List r1Blocks = blocks( r1 ); - List r2Blocks = blocks( r2 ); - if ( r1Blocks.size() != r2Blocks.size() ) - { - return false; - } - for ( int i = 0; i < r1Blocks.size(); i++ ) - { - PropertyBlock r1Block = r1Blocks.get( i ); - PropertyBlock r2Block = r2Blocks.get( i ); - if ( !Arrays.equals( r1Block.getValueBlocks(), r2Block.getValueBlocks() ) ) - { - return false; - } - } - return true; - } - - static List blocks( PropertyRecord record ) - { - List result = new ArrayList<>(); - while ( record.hasNext() ) - { - result.add( record.next() ); - } - return result; - } - } -} diff --git a/tools/src/main/java/org/neo4j/tools/txlog/CommittedRecords.java b/tools/src/main/java/org/neo4j/tools/txlog/CommittedRecords.java index 22e858cda7b3d..26551635a825f 100644 --- a/tools/src/main/java/org/neo4j/tools/txlog/CommittedRecords.java +++ b/tools/src/main/java/org/neo4j/tools/txlog/CommittedRecords.java @@ -24,6 +24,7 @@ import org.neo4j.kernel.impl.store.record.Abstract64BitRecord; import org.neo4j.kernel.impl.store.record.NodeRecord; import org.neo4j.kernel.impl.store.record.PropertyRecord; +import org.neo4j.tools.txlog.checktypes.CheckType; /** * Contains mapping from entity id ({@link NodeRecord#getId()}, {@link PropertyRecord#getId()}, ...) to diff --git a/tools/src/main/java/org/neo4j/tools/txlog/checktypes/CheckType.java b/tools/src/main/java/org/neo4j/tools/txlog/checktypes/CheckType.java new file mode 100644 index 0000000000000..5790c57ebd588 --- /dev/null +++ b/tools/src/main/java/org/neo4j/tools/txlog/checktypes/CheckType.java @@ -0,0 +1,57 @@ +/* + * 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 . + */ +package org.neo4j.tools.txlog.checktypes; + +import org.neo4j.kernel.impl.store.record.Abstract64BitRecord; +import org.neo4j.kernel.impl.transaction.command.Command; +import org.neo4j.kernel.impl.transaction.command.Command.NodeCommand; +import org.neo4j.kernel.impl.transaction.command.Command.PropertyCommand; + +/** + * Type of command ({@link NodeCommand}, {@link PropertyCommand}, ...) to check during transaction log verification. + * This class exists to mitigate the absence of interfaces for commands with before and after state. + * It also provides an alternative equality check instead of {@link Abstract64BitRecord#equals(Object)} that only + * checks {@linkplain Abstract64BitRecord#getLongId() entity id}. + * + * @param the type of command to check + * @param the type of records that this command contains + */ +public abstract class CheckType +{ + private final Class recordClass; + + CheckType( Class recordClass ) + { + this.recordClass = recordClass; + } + + public Class commandClass() + { + return recordClass; + } + + public abstract R before( C command ); + + public abstract R after( C command ); + + public abstract boolean equal( R record1, R record2 ); + + public abstract String name(); +} diff --git a/tools/src/main/java/org/neo4j/tools/txlog/checktypes/CheckTypes.java b/tools/src/main/java/org/neo4j/tools/txlog/checktypes/CheckTypes.java new file mode 100644 index 0000000000000..75ccea1ae194d --- /dev/null +++ b/tools/src/main/java/org/neo4j/tools/txlog/checktypes/CheckTypes.java @@ -0,0 +1,14 @@ +package org.neo4j.tools.txlog.checktypes; + +import org.neo4j.kernel.impl.store.record.Abstract64BitRecord; +import org.neo4j.kernel.impl.transaction.command.Command; + +public class CheckTypes +{ + public static final NodeCheckType NODE = new NodeCheckType(); + public static final PropertyCheckType PROPERTY = new PropertyCheckType(); + + @SuppressWarnings( "unchecked" ) + public static final CheckType[] CHECK_TYPES = + new CheckType[]{NODE, PROPERTY}; +} diff --git a/tools/src/main/java/org/neo4j/tools/txlog/checktypes/NodeCheckType.java b/tools/src/main/java/org/neo4j/tools/txlog/checktypes/NodeCheckType.java new file mode 100644 index 0000000000000..7109d9abfd822 --- /dev/null +++ b/tools/src/main/java/org/neo4j/tools/txlog/checktypes/NodeCheckType.java @@ -0,0 +1,46 @@ +package org.neo4j.tools.txlog.checktypes; + +import java.util.Objects; + +import org.neo4j.kernel.impl.store.record.NodeRecord; +import org.neo4j.kernel.impl.transaction.command.Command; + +class NodeCheckType extends CheckType +{ + NodeCheckType() + { + super( Command.NodeCommand.class ); + } + + @Override + public NodeRecord before( Command.NodeCommand command ) + { + return command.getBefore(); + } + + @Override + public NodeRecord after( Command.NodeCommand command ) + { + return command.getAfter(); + } + + @Override + public boolean equal( NodeRecord record1, NodeRecord record2 ) + { + Objects.requireNonNull( record1 ); + Objects.requireNonNull( record2 ); + + return record1.getId() == record2.getId() && + record1.inUse() == record2.inUse() && + record1.getNextProp() == record2.getNextProp() && + record1.getNextRel() == record2.getNextRel() && + record1.isDense() == record2.isDense() && + record1.getLabelField() == record2.getLabelField(); + } + + @Override + public String name() + { + return "node"; + } +} diff --git a/tools/src/main/java/org/neo4j/tools/txlog/checktypes/PropertyCheckType.java b/tools/src/main/java/org/neo4j/tools/txlog/checktypes/PropertyCheckType.java new file mode 100644 index 0000000000000..9348d58edd18f --- /dev/null +++ b/tools/src/main/java/org/neo4j/tools/txlog/checktypes/PropertyCheckType.java @@ -0,0 +1,97 @@ +package org.neo4j.tools.txlog.checktypes; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import org.neo4j.kernel.impl.store.record.PropertyBlock; +import org.neo4j.kernel.impl.store.record.PropertyRecord; +import org.neo4j.kernel.impl.transaction.command.Command; + +class PropertyCheckType extends CheckType +{ + PropertyCheckType() + { + super( Command.PropertyCommand.class ); + } + + @Override + public PropertyRecord before( Command.PropertyCommand command ) + { + return command.getBefore(); + } + + @Override + public PropertyRecord after( Command.PropertyCommand command ) + { + return command.getAfter(); + } + + @Override + public boolean equal( PropertyRecord record1, PropertyRecord record2 ) + { + Objects.requireNonNull( record1 ); + Objects.requireNonNull( record2 ); + + if ( record1.getId() != record2.getId() ) + { + return false; + } + if ( record1.inUse() != record2.inUse() ) + { + return false; + } + if ( !record1.inUse() ) + { + return true; + } + return record1.isNodeSet() == record2.isNodeSet() && + record1.isRelSet() == record2.isRelSet() && + record1.getNodeId() == record2.getNodeId() && + record1.getRelId() == record2.getRelId() && + record1.getNextProp() == record2.getNextProp() && + record1.getPrevProp() == record2.getPrevProp() && + blocksEqual( record1, record2 ); + } + + @Override + public String name() + { + return "property"; + } + + private static boolean blocksEqual( PropertyRecord r1, PropertyRecord r2 ) + { + if ( r1.size() != r2.size() ) + { + return false; + } + List r1Blocks = blocks( r1 ); + List r2Blocks = blocks( r2 ); + if ( r1Blocks.size() != r2Blocks.size() ) + { + return false; + } + for ( int i = 0; i < r1Blocks.size(); i++ ) + { + PropertyBlock r1Block = r1Blocks.get( i ); + PropertyBlock r2Block = r2Blocks.get( i ); + if ( !Arrays.equals( r1Block.getValueBlocks(), r2Block.getValueBlocks() ) ) + { + return false; + } + } + return true; + } + + private static List blocks( PropertyRecord record ) + { + List result = new ArrayList<>(); + while ( record.hasNext() ) + { + result.add( record.next() ); + } + return result; + } +} diff --git a/tools/src/test/java/org/neo4j/tools/txlog/CheckTxLogsTest.java b/tools/src/test/java/org/neo4j/tools/txlog/CheckTxLogsTest.java index cafd59d7b5556..68e48cce699fe 100644 --- a/tools/src/test/java/org/neo4j/tools/txlog/CheckTxLogsTest.java +++ b/tools/src/test/java/org/neo4j/tools/txlog/CheckTxLogsTest.java @@ -52,6 +52,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.neo4j.tools.txlog.checktypes.CheckTypes.NODE; +import static org.neo4j.tools.txlog.checktypes.CheckTypes.PROPERTY; public class CheckTxLogsTest { @@ -96,7 +98,7 @@ public void shouldReportNodeInconsistenciesFromSingleLog() throws IOException CheckTxLogs checker = new CheckTxLogs( fsRule.get() ); // When - checker.scan( new File[]{log}, CheckType.NODE, handler ); + checker.scan( new File[]{log}, handler, NODE ); // Then assertEquals( 1, handler.inconsistencies.size() ); @@ -155,7 +157,7 @@ public void shouldReportNodeInconsistenciesFromDifferentLogs() throws IOExceptio CheckTxLogs checker = new CheckTxLogs( fsRule.get() ); // When - checker.scan( new File[]{log1, log2, log3}, CheckType.NODE, handler ); + checker.scan( new File[]{log1, log2, log3}, handler, NODE ); // Then assertEquals( 2, handler.inconsistencies.size() ); @@ -209,7 +211,7 @@ public void shouldReportPropertyInconsistenciesFromSingleLog() throws IOExceptio CheckTxLogs checker = new CheckTxLogs( fsRule.get() ); // When - checker.scan( new File[]{log}, CheckType.PROPERTY, handler ); + checker.scan( new File[]{log}, handler, PROPERTY ); // Then assertEquals( 1, handler.inconsistencies.size() ); @@ -272,7 +274,7 @@ public void shouldReportPropertyInconsistenciesFromDifferentLogs() throws IOExce CheckTxLogs checker = new CheckTxLogs( fsRule.get() ); // When - checker.scan( new File[]{log1, log2, log3}, CheckType.PROPERTY, handler ); + checker.scan( new File[]{log1, log2, log3}, handler, PROPERTY ); // Then assertEquals( 2, handler.inconsistencies.size() );