Skip to content

Commit

Permalink
Fail rebuild from logs if final target store is inconsistent.
Browse files Browse the repository at this point in the history
  • Loading branch information
MishaDemianenko committed Mar 6, 2017
1 parent f1a3ba2 commit 8e84073
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 50 deletions.
31 changes: 22 additions & 9 deletions tools/src/main/java/org/neo4j/tools/rebuild/RebuildFromLogs.java
Expand Up @@ -27,8 +27,10 @@


import org.neo4j.com.storecopy.ExternallyManagedPageCache; import org.neo4j.com.storecopy.ExternallyManagedPageCache;
import org.neo4j.consistency.ConsistencyCheckService; import org.neo4j.consistency.ConsistencyCheckService;
import org.neo4j.consistency.checking.InconsistentStoreException;
import org.neo4j.consistency.checking.full.ConsistencyCheckIncompleteException; import org.neo4j.consistency.checking.full.ConsistencyCheckIncompleteException;
import org.neo4j.consistency.checking.full.FullCheck; import org.neo4j.consistency.checking.full.FullCheck;
import org.neo4j.consistency.report.ConsistencySummaryStatistics;
import org.neo4j.consistency.statistics.Statistics; import org.neo4j.consistency.statistics.Statistics;
import org.neo4j.cursor.IOCursor; import org.neo4j.cursor.IOCursor;
import org.neo4j.graphdb.DependencyResolver; import org.neo4j.graphdb.DependencyResolver;
Expand Down Expand Up @@ -62,7 +64,7 @@
import org.neo4j.kernel.impl.transaction.log.entry.LogEntryReader; import org.neo4j.kernel.impl.transaction.log.entry.LogEntryReader;
import org.neo4j.kernel.impl.transaction.log.entry.VersionAwareLogEntryReader; import org.neo4j.kernel.impl.transaction.log.entry.VersionAwareLogEntryReader;
import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.kernel.internal.GraphDatabaseAPI;
import org.neo4j.logging.NullLog; import org.neo4j.logging.FormattedLog;


import static java.lang.String.format; import static java.lang.String.format;
import static org.neo4j.kernel.impl.transaction.log.LogVersionBridge.NO_MORE_CHANNELS; import static org.neo4j.kernel.impl.transaction.log.LogVersionBridge.NO_MORE_CHANNELS;
Expand All @@ -85,7 +87,7 @@ public RebuildFromLogs( FileSystemAbstraction fs )
this.fs = fs; this.fs = fs;
} }


public static void main( String[] args ) throws Exception public static void main( String[] args ) throws Exception, InconsistentStoreException
{ {
if ( args == null ) if ( args == null )
{ {
Expand Down Expand Up @@ -142,7 +144,7 @@ private static boolean directoryContainsDb( Path path )
return Files.exists( path.resolve( MetaDataStore.DEFAULT_NAME ) ); return Files.exists( path.resolve( MetaDataStore.DEFAULT_NAME ) );
} }


public void rebuild( File source, File target, long txId ) throws Exception public void rebuild( File source, File target, long txId ) throws Exception, InconsistentStoreException
{ {
try ( PageCache pageCache = StandalonePageCacheFactory.createPageCache( fs ) ) try ( PageCache pageCache = StandalonePageCacheFactory.createPageCache( fs ) )
{ {
Expand Down Expand Up @@ -177,10 +179,15 @@ public void rebuild( File source, File target, long txId ) throws Exception
MetaDataStore.setRecord( pageCache, new File( target, MetaDataStore.DEFAULT_NAME ), MetaDataStore.setRecord( pageCache, new File( target, MetaDataStore.DEFAULT_NAME ),
MetaDataStore.Position.LAST_TRANSACTION_ID, lastTxId ); MetaDataStore.Position.LAST_TRANSACTION_ID, lastTxId );


try ( ConsistencyChecker checker = new ConsistencyChecker( target, pageCache ) ) checkConsistency( target, pageCache );
{ }
checker.checkConsistency(); }
}
void checkConsistency( File target, PageCache pageCache ) throws Exception, InconsistentStoreException
{
try ( ConsistencyChecker checker = new ConsistencyChecker( target, pageCache ) )
{
checker.checkConsistency();
} }
} }


Expand Down Expand Up @@ -280,15 +287,21 @@ private static class ConsistencyChecker implements AutoCloseable
this.indexes = resolver.resolveDependency( SchemaIndexProvider.class ); this.indexes = resolver.resolveDependency( SchemaIndexProvider.class );
} }


private void checkConsistency() throws ConsistencyCheckIncompleteException private void checkConsistency() throws ConsistencyCheckIncompleteException, InconsistentStoreException
{ {
StoreAccess nativeStores = new StoreAccess( graphdb.getDependencyResolver() StoreAccess nativeStores = new StoreAccess( graphdb.getDependencyResolver()
.resolveDependency( RecordStorageEngine.class ).testAccessNeoStores() ).initialize(); .resolveDependency( RecordStorageEngine.class ).testAccessNeoStores() ).initialize();
DirectStoreAccess stores = new DirectStoreAccess( nativeStores, labelScanStore, indexes ); DirectStoreAccess stores = new DirectStoreAccess( nativeStores, labelScanStore, indexes );
FullCheck fullCheck = new FullCheck( tuningConfiguration, ProgressMonitorFactory.textual( System.err ), FullCheck fullCheck = new FullCheck( tuningConfiguration, ProgressMonitorFactory.textual( System.err ),
Statistics.NONE, ConsistencyCheckService.defaultConsistencyCheckThreadsNumber() ); Statistics.NONE, ConsistencyCheckService.defaultConsistencyCheckThreadsNumber() );


fullCheck.execute( stores, NullLog.getInstance() ); ConsistencySummaryStatistics summaryStatistics =
fullCheck.execute( stores, FormattedLog.toOutputStream( System.err ) );
if ( !summaryStatistics.isConsistent() )
{
throw new InconsistentStoreException( summaryStatistics );
}

} }


@Override @Override
Expand Down
105 changes: 64 additions & 41 deletions tools/src/test/java/org/neo4j/tools/rebuild/RebuildFromLogsTest.java
Expand Up @@ -21,22 +21,27 @@


import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.RuleChain; import org.junit.rules.RuleChain;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Parameterized; import org.junit.runners.Parameterized;


import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set; import java.util.Set;


import org.neo4j.consistency.checking.InconsistentStoreException;
import org.neo4j.consistency.report.ConsistencySummaryStatistics;
import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Node;
import org.neo4j.helpers.collection.Iterables; import org.neo4j.helpers.collection.Iterables;
import org.neo4j.io.fs.FileSystemAbstraction;
import org.neo4j.io.fs.FileUtils; import org.neo4j.io.fs.FileUtils;
import org.neo4j.io.pagecache.PageCache;
import org.neo4j.kernel.impl.store.MetaDataStore; import org.neo4j.kernel.impl.store.MetaDataStore;
import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.kernel.internal.GraphDatabaseAPI;
import org.neo4j.test.DbRepresentation; import org.neo4j.test.DbRepresentation;
Expand All @@ -54,28 +59,26 @@ public class RebuildFromLogsTest
private final TestDirectory dir = TestDirectory.testDirectory(); private final TestDirectory dir = TestDirectory.testDirectory();
private final SuppressOutput suppressOutput = SuppressOutput.suppressAll(); private final SuppressOutput suppressOutput = SuppressOutput.suppressAll();
private final DefaultFileSystemRule fileSystemRule = new DefaultFileSystemRule(); private final DefaultFileSystemRule fileSystemRule = new DefaultFileSystemRule();
private final ExpectedException expectedException = ExpectedException.none();


@Rule @Rule
public RuleChain ruleChain = RuleChain.outerRule( dir ) public RuleChain ruleChain = RuleChain.outerRule( dir )
.around( suppressOutput ).around( fileSystemRule ); .around( suppressOutput ).around( fileSystemRule ).around( expectedException );

private final Transaction[] work;

@Parameterized.Parameters(name = "{0}")
public static Collection<WorkLog> commands()
{
return WorkLog.combinations();
}


@Test @Test
public void shouldRebuildFromLog() throws Exception public void shouldRebuildFromLog() throws Exception, InconsistentStoreException
{ {
// given // given
File prototypePath = new File( dir.graphDbDir(), "prototype" ); File prototypePath = new File( dir.graphDbDir(), "prototype" );
GraphDatabaseService prototype = db( prototypePath ); populatePrototype( prototypePath );
try
{
for ( Transaction transaction : work )
{
transaction.applyTo( prototype );
}
}
finally
{
prototype.shutdown();
}


// when // when
File rebuildPath = new File( dir.graphDbDir(), "rebuild" ); File rebuildPath = new File( dir.graphDbDir(), "rebuild" );
Expand All @@ -85,30 +88,25 @@ public void shouldRebuildFromLog() throws Exception
assertEquals( getDbRepresentation( prototypePath ), getDbRepresentation( rebuildPath ) ); assertEquals( getDbRepresentation( prototypePath ), getDbRepresentation( rebuildPath ) );
} }


private DbRepresentation getDbRepresentation( File path ) @Test
public void failRebuildFromLogIfStoreIsInconsistentAfterRebuild() throws InconsistentStoreException, Exception
{ {
return DbRepresentation.of( path ); File prototypePath = new File( dir.graphDbDir(), "prototype" );
populatePrototype( prototypePath );

// when
File rebuildPath = new File( dir.graphDbDir(), "rebuild" );
expectedException.expect( InconsistentStoreException.class );
RebuildFromLogs rebuildFromLogs = new TestRebuildFromLogs( fileSystemRule.get() );
rebuildFromLogs.rebuild( prototypePath, rebuildPath, BASE_TX_ID );
} }


@Test @Test
public void shouldRebuildFromLogUpToATx() throws Exception public void shouldRebuildFromLogUpToATx() throws Exception, InconsistentStoreException
{ {
// given // given
File prototypePath = new File( dir.graphDbDir(), "prototype" ); File prototypePath = new File( dir.graphDbDir(), "prototype" );
GraphDatabaseAPI prototype = db( prototypePath ); long txId = populatePrototype( prototypePath );
long txId;
try
{
for ( Transaction transaction : work )
{
transaction.applyTo( prototype );
}
}
finally
{
txId = prototype.getDependencyResolver().resolveDependency( MetaDataStore.class ).getLastCommittedTransactionId();
prototype.shutdown();
}


File copy = new File( dir.graphDbDir(), "copy" ); File copy = new File( dir.graphDbDir(), "copy" );
FileUtils.copyRecursively( prototypePath, copy ); FileUtils.copyRecursively( prototypePath, copy );
Expand All @@ -131,6 +129,30 @@ public void shouldRebuildFromLogUpToATx() throws Exception
assertEquals( getDbRepresentation( prototypePath ), getDbRepresentation( rebuildPath ) ); assertEquals( getDbRepresentation( prototypePath ), getDbRepresentation( rebuildPath ) );
} }


private long populatePrototype( File prototypePath )
{
GraphDatabaseAPI prototype = db( prototypePath );
long txId;
try
{
for ( Transaction transaction : work )
{
transaction.applyTo( prototype );
}
}
finally
{
txId = prototype.getDependencyResolver().resolveDependency( MetaDataStore.class ).getLastCommittedTransactionId();
prototype.shutdown();
}
return txId;
}

private DbRepresentation getDbRepresentation( File path )
{
return DbRepresentation.of( path );
}

private GraphDatabaseAPI db( File rebuiltPath ) private GraphDatabaseAPI db( File rebuiltPath )
{ {
return (GraphDatabaseAPI) new TestGraphDatabaseFactory().newEmbeddedDatabase( rebuiltPath ); return (GraphDatabaseAPI) new TestGraphDatabaseFactory().newEmbeddedDatabase( rebuiltPath );
Expand Down Expand Up @@ -194,7 +216,7 @@ private static Node firstNode( GraphDatabaseService graphDb )


private final Transaction[] dependencies; private final Transaction[] dependencies;


private Transaction( Transaction... dependencies ) Transaction( Transaction... dependencies )
{ {
this.dependencies = dependencies; this.dependencies = dependencies;
} }
Expand Down Expand Up @@ -275,21 +297,22 @@ private WorkLog extend( Transaction transaction )
} }
} }


private final Transaction[] work;

public RebuildFromLogsTest( WorkLog work ) public RebuildFromLogsTest( WorkLog work )
{ {
this.work = work.transactions(); this.work = work.transactions();
} }


@Parameterized.Parameters(name = "{0}") private class TestRebuildFromLogs extends RebuildFromLogs
public static List<Object[]> commands()
{ {
List<Object[]> commands = new ArrayList<>(); TestRebuildFromLogs( FileSystemAbstraction fs )
for ( WorkLog combination : WorkLog.combinations() ) {
super( fs );
}

@Override
void checkConsistency( File target, PageCache pageCache ) throws Exception, InconsistentStoreException
{ {
commands.add( new Object[]{combination} ); throw new InconsistentStoreException( new ConsistencySummaryStatistics() );
} }
return commands;
} }
} }

0 comments on commit 8e84073

Please sign in to comment.