Skip to content

Commit

Permalink
Adapt Load and Dump commands for possibility to have have transaction
Browse files Browse the repository at this point in the history
logs in a non default location.
  • Loading branch information
MishaDemianenko committed Nov 16, 2017
1 parent 4cb2df0 commit 074b824
Show file tree
Hide file tree
Showing 14 changed files with 378 additions and 104 deletions.
Expand Up @@ -63,6 +63,20 @@ public static Path canonicalPath( File file ) throws IllegalArgumentException
} }
} }


public static boolean isSameOrChildFile( File parent, File candidate )
{
Path canonicalCandidate = canonicalPath( candidate );
Path canonicalParentPath = canonicalPath( parent );
return canonicalCandidate.startsWith( canonicalParentPath );
}

public static boolean isSameOrChildPath( Path parent, Path candidate )
{
Path normalizedCandidate = candidate.normalize();
Path normalizedParent = parent.normalize();
return normalizedCandidate.startsWith( normalizedParent );
}

public static void checkLock( Path databaseDirectory ) throws CommandFailed public static void checkLock( Path databaseDirectory ) throws CommandFailed
{ {
try ( FileSystemAbstraction fileSystem = new DefaultFileSystemAbstraction(); try ( FileSystemAbstraction fileSystem = new DefaultFileSystemAbstraction();
Expand Down
Expand Up @@ -42,6 +42,7 @@
import static java.lang.String.format; import static java.lang.String.format;
import static org.neo4j.commandline.Util.canonicalPath; import static org.neo4j.commandline.Util.canonicalPath;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.database_path; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.database_path;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.logical_logs_location;


public class DumpCommand implements AdminCommand public class DumpCommand implements AdminCommand
{ {
Expand All @@ -66,7 +67,10 @@ public void execute( String[] args ) throws IncorrectUsage, CommandFailed
{ {
String database = arguments.parse( args ).get( "database" ); String database = arguments.parse( args ).get( "database" );
Path archive = calculateArchive( database, arguments.getMandatoryPath( "to" ) ); Path archive = calculateArchive( database, arguments.getMandatoryPath( "to" ) );
Path databaseDirectory = canonicalPath( toDatabaseDirectory( database ) );
Config config = buildConfig( database );
Path databaseDirectory = canonicalPath( getDatabaseDirectory( config ) );
Path transactionLogsDirectory = canonicalPath( getTransactionalLogsDirectory( config ) );


try try
{ {
Expand All @@ -79,7 +83,7 @@ public void execute( String[] args ) throws IncorrectUsage, CommandFailed


try ( Closeable ignored = StoreLockChecker.check( databaseDirectory ) ) try ( Closeable ignored = StoreLockChecker.check( databaseDirectory ) )
{ {
dump( database, databaseDirectory, archive ); dump( database, databaseDirectory, transactionLogsDirectory, archive );
} }
catch ( StoreLockException e ) catch ( StoreLockException e )
{ {
Expand All @@ -97,25 +101,36 @@ public void execute( String[] args ) throws IncorrectUsage, CommandFailed
} }
} }


private Path toDatabaseDirectory( String databaseName ) private Path getDatabaseDirectory( Config config )
{
return config.get( database_path ).toPath();
}

private Path getTransactionalLogsDirectory( Config config )
{
return config.get( logical_logs_location ).toPath();
}

private Config buildConfig( String databaseName )
{ {
return Config.fromFile( configDir.resolve( Config.DEFAULT_CONFIG_FILE_NAME ) ) return Config.fromFile( configDir.resolve( Config.DEFAULT_CONFIG_FILE_NAME ) )
.withHome( homeDir ) .withHome( homeDir )
.withConnectorsDisabled() .withConnectorsDisabled()
.withSetting( GraphDatabaseSettings.active_database, databaseName ) .withSetting( GraphDatabaseSettings.active_database, databaseName )
.build().get( database_path ).toPath(); .build();
} }


private Path calculateArchive( String database, Path to ) private Path calculateArchive( String database, Path to )
{ {
return Files.isDirectory( to ) ? to.resolve( database + ".dump" ) : to; return Files.isDirectory( to ) ? to.resolve( database + ".dump" ) : to;
} }


private void dump( String database, Path databaseDirectory, Path archive ) throws CommandFailed private void dump( String database, Path databaseDirectory, Path transactionalLogsDirectory, Path archive )
throws CommandFailed
{ {
try try
{ {
dumper.dump( databaseDirectory, archive, this::isStoreLock ); dumper.dump( databaseDirectory, transactionalLogsDirectory, archive, this::isStoreLock );
} }
catch ( FileAlreadyExistsException e ) catch ( FileAlreadyExistsException e )
{ {
Expand Down
Expand Up @@ -41,8 +41,10 @@
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static org.neo4j.commandline.Util.canonicalPath; import static org.neo4j.commandline.Util.canonicalPath;
import static org.neo4j.commandline.Util.checkLock; import static org.neo4j.commandline.Util.checkLock;
import static org.neo4j.commandline.Util.isSameOrChildPath;
import static org.neo4j.commandline.Util.wrapIOException; import static org.neo4j.commandline.Util.wrapIOException;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.database_path; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.database_path;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.logical_logs_location;


public class LoadCommand implements AdminCommand public class LoadCommand implements AdminCommand
{ {
Expand Down Expand Up @@ -74,29 +76,46 @@ public void execute( String[] args ) throws IncorrectUsage, CommandFailed
String database = arguments.get( "database" ); String database = arguments.get( "database" );
boolean force = arguments.getBoolean( "force" ); boolean force = arguments.getBoolean( "force" );


Path databaseDirectory = canonicalPath( toDatabaseDirectory( database ) ); Config config = buildConfig( database );


deleteIfNecessary( databaseDirectory, force ); Path databaseDirectory = canonicalPath( getDatabaseDirectory( config ) );
load( archive, database, databaseDirectory ); Path transactionLogsDirectory = canonicalPath( getTransactionalLogsDirectory( config ) );

deleteIfNecessary( databaseDirectory, transactionLogsDirectory, force );
load( archive, database, databaseDirectory, transactionLogsDirectory );
}

private Path getDatabaseDirectory( Config config )
{
return config.get( database_path ).toPath();
}

private Path getTransactionalLogsDirectory( Config config )
{
return config.get( logical_logs_location ).toPath();
} }


private Path toDatabaseDirectory( String databaseName ) private Config buildConfig( String databaseName )
{ {
return Config.fromFile( configDir.resolve( Config.DEFAULT_CONFIG_FILE_NAME ) ) return Config.fromFile( configDir.resolve( Config.DEFAULT_CONFIG_FILE_NAME ) )
.withHome( homeDir ) .withHome( homeDir )
.withConnectorsDisabled() .withConnectorsDisabled()
.withSetting( GraphDatabaseSettings.active_database, databaseName ) .withSetting( GraphDatabaseSettings.active_database, databaseName )
.build().get( database_path ).toPath(); .build();
} }


private void deleteIfNecessary( Path databaseDirectory, boolean force ) throws CommandFailed private void deleteIfNecessary( Path databaseDirectory, Path transactionLogsDirectory, boolean force ) throws CommandFailed
{ {
try try
{ {
if ( force ) if ( force )
{ {
checkLock( databaseDirectory ); checkLock( databaseDirectory );
FileUtils.deletePathRecursively( databaseDirectory ); FileUtils.deletePathRecursively( databaseDirectory );
if ( !isSameOrChildPath( databaseDirectory, transactionLogsDirectory ) )
{
FileUtils.deletePathRecursively( transactionLogsDirectory );
}
} }
} }
catch ( IOException e ) catch ( IOException e )
Expand All @@ -105,11 +124,11 @@ private void deleteIfNecessary( Path databaseDirectory, boolean force ) throws C
} }
} }


private void load( Path archive, String database, Path databaseDirectory ) throws CommandFailed private void load( Path archive, String database, Path databaseDirectory, Path transactionLogsDirectory ) throws CommandFailed
{ {
try try
{ {
loader.load( archive, databaseDirectory ); loader.load( archive, databaseDirectory, transactionLogsDirectory );
} }
catch ( NoSuchFileException e ) catch ( NoSuchFileException e )
{ {
Expand Down
36 changes: 24 additions & 12 deletions community/dbms/src/main/java/org/neo4j/dbms/archive/Dumper.java
Expand Up @@ -19,18 +19,19 @@
*/ */
package org.neo4j.dbms.archive; package org.neo4j.dbms.archive;


import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;

import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.function.Predicate; import java.util.function.Predicate;


import org.apache.commons.compress.archivers.ArchiveEntry; import org.neo4j.commandline.Util;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;

import org.neo4j.function.ThrowingAction; import org.neo4j.function.ThrowingAction;


import static org.neo4j.dbms.archive.Utils.checkWritableDirectory; import static org.neo4j.dbms.archive.Utils.checkWritableDirectory;
Expand All @@ -45,20 +46,31 @@


public class Dumper public class Dumper
{ {
public void dump( Path root, Path archive, Predicate<Path> exclude ) throws IOException public void dump( Path dbPath, Path transactionalLogsPath, Path archive, Predicate<Path> exclude )
throws IOException
{ {
checkWritableDirectory( archive.getParent() ); checkWritableDirectory( archive.getParent() );
try ( ArchiveOutputStream stream = openArchiveOut( archive ) ) try ( ArchiveOutputStream stream = openArchiveOut( archive ) )
{ {
Files.walkFileTree( root, visitPath( dbPath, exclude, stream );
onlyMatching( not( exclude ), if ( !Util.isSameOrChildPath( dbPath, transactionalLogsPath ) )
throwExceptions( {
onDirectory( dir -> dumpDirectory( root, stream, dir ), visitPath( transactionalLogsPath, exclude, stream );
onFile( file -> dumpFile( root, stream, file ), }
justContinue() ) ) ) ) );
} }
} }


private void visitPath( Path transactionalLogsPath, Predicate<Path> exclude, ArchiveOutputStream stream )
throws IOException
{
Files.walkFileTree( transactionalLogsPath,
onlyMatching( not( exclude ),
throwExceptions(
onDirectory( dir -> dumpDirectory( transactionalLogsPath, stream, dir ),
onFile( file -> dumpFile( transactionalLogsPath, stream, file ),
justContinue() ) ) ) ) );
}

private static ArchiveOutputStream openArchiveOut( Path archive ) throws IOException private static ArchiveOutputStream openArchiveOut( Path archive ) throws IOException
{ {
// StandardOpenOption.CREATE_NEW is important here because it atomically asserts that the file doesn't // StandardOpenOption.CREATE_NEW is important here because it atomically asserts that the file doesn't
Expand Down
52 changes: 41 additions & 11 deletions community/dbms/src/main/java/org/neo4j/dbms/archive/Loader.java
Expand Up @@ -19,41 +19,71 @@
*/ */
package org.neo4j.dbms.archive; package org.neo4j.dbms.archive;


import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;

import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystemException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;


import org.apache.commons.compress.archivers.ArchiveEntry; import org.neo4j.kernel.impl.transaction.log.files.TransactionLogFiles;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;


import static java.nio.file.Files.exists; import static java.nio.file.Files.exists;

import static org.neo4j.dbms.archive.Utils.checkWritableDirectory; import static org.neo4j.dbms.archive.Utils.checkWritableDirectory;


public class Loader public class Loader
{ {
public void load( Path archive, Path destination ) throws IOException, IncorrectFormat public void load( Path archive, Path databaseDestination, Path transactionLogsDirectory ) throws IOException, IncorrectFormat
{ {
if ( exists( destination ) ) validatePath( databaseDestination );
{ validatePath( transactionLogsDirectory );
throw new FileAlreadyExistsException( destination.toString() );
} createDestination( databaseDestination );
checkWritableDirectory( destination.getParent() ); createDestination( transactionLogsDirectory );

try ( ArchiveInputStream stream = openArchiveIn( archive ) ) try ( ArchiveInputStream stream = openArchiveIn( archive ) )
{ {
ArchiveEntry entry; ArchiveEntry entry;
while ( (entry = nextEntry( stream, archive )) != null ) while ( (entry = nextEntry( stream, archive )) != null )
{ {
Path destination = determineEntryDestination( entry, databaseDestination, transactionLogsDirectory );
loadEntry( destination, stream, entry ); loadEntry( destination, stream, entry );
} }
} }
} }


private void createDestination( Path destination ) throws IOException
{
if ( !destination.toFile().exists() )
{
Files.createDirectories( destination );
}
}

private void validatePath( Path path ) throws FileSystemException
{
if ( exists( path ) )
{
throw new FileAlreadyExistsException( path.toString() );
}
checkWritableDirectory( path.getParent() );
}

private static Path determineEntryDestination( ArchiveEntry entry, Path databaseDestination,
Path transactionLogsDirectory )
{
String entryName = Paths.get( entry.getName() ).getFileName().toString();
return TransactionLogFiles.DEFAULT_FILENAME_FILTER.accept( null, entryName ) ? transactionLogsDirectory
: databaseDestination;
}

private ArchiveEntry nextEntry( ArchiveInputStream stream, Path archive ) throws IncorrectFormat private ArchiveEntry nextEntry( ArchiveInputStream stream, Path archive ) throws IncorrectFormat
{ {
try try
Expand Down

0 comments on commit 074b824

Please sign in to comment.