Skip to content

Commit

Permalink
Introduce new commands for restoring new/existing cluster
Browse files Browse the repository at this point in the history
These commands are wrappers around restore, generate-cluster-seed and
core-convert and make the interface simpler and more task-oriented for
the operator.
  • Loading branch information
Mark Needham committed Jun 28, 2016
1 parent 9c79c33 commit e4906a2
Show file tree
Hide file tree
Showing 13 changed files with 511 additions and 51 deletions.
Expand Up @@ -25,20 +25,20 @@

import org.neo4j.kernel.impl.store.StoreId;

public class SourceMetadata
public class ClusterSeed
{
private final StoreId before;
private final StoreId after;
private final long lastTxId;

public SourceMetadata( StoreId before, StoreId after, long lastTxId )
public ClusterSeed( StoreId before, StoreId after, long lastTxId )
{
this.before = before;
this.after = after;
this.lastTxId = lastTxId;
}

public static SourceMetadata create( String rawConversionId )
public static ClusterSeed create( String rawConversionId )
{
byte[] bytes = Base64.getDecoder().decode( rawConversionId );
ByteBuffer buffer = ByteBuffer.wrap( bytes );
Expand All @@ -47,7 +47,7 @@ public static SourceMetadata create( String rawConversionId )
StoreId before = readStoreId( buffer );
StoreId after = readStoreId( buffer );

return new SourceMetadata( before, after, txId );
return new ClusterSeed( before, after, txId );
}

private static StoreId readStoreId( ByteBuffer buffer )
Expand Down Expand Up @@ -104,7 +104,7 @@ public boolean equals( Object o )
{
return false;
}
SourceMetadata that = (SourceMetadata) o;
ClusterSeed that = (ClusterSeed) o;
return lastTxId == that.lastTxId &&
storeIdEquals( before, that.before ) &&
storeIdEquals( after, that.after );
Expand Down
Expand Up @@ -23,11 +23,11 @@

public class ConversionVerifier
{
public void conversionGuard( SourceMetadata source,
TargetMetadata target )
public void conversionGuard( ClusterSeed source,
StoreMetadata target )
{
StoreId sourceBefore = source.before();
StoreId targetBefore = target.before();
StoreId targetBefore = target.storeId();

long sourceLastTxId = source.lastTxId();
long targetLastTxId = target.lastTxId();
Expand Down
Expand Up @@ -82,34 +82,35 @@ public ConvertClassicStoreCommand( ConversionVerifier conversionVerifier )
this.conversionVerifier = conversionVerifier;
}

public void convert( File databaseDir, String recordFormat, String conversionId ) throws Throwable
public void convert( File databaseDir, String recordFormat, String conversionId ) throws IOException, TransactionFailureException

{
SourceMetadata metadata = SourceMetadata.create( conversionId );
ClusterSeed metadata = ClusterSeed.create( conversionId );
verify( databaseDir, metadata );
changeStoreId( databaseDir, metadata );
appendNullTransactionLogEntryToSetRaftIndexToMinusOne( databaseDir, recordFormat );
addIdAllocationState( databaseDir );
}

private void verify( File databaseDir, SourceMetadata metadata ) throws IOException
private void verify( File databaseDir, ClusterSeed metadata ) throws IOException
{
TargetMetadata targetMetadata = targetMetadata( databaseDir );
conversionVerifier.conversionGuard( metadata, targetMetadata );
StoreMetadata storeMetadata = targetMetadata( databaseDir );
conversionVerifier.conversionGuard( metadata, storeMetadata );
}

private TargetMetadata targetMetadata( File databaseDir ) throws IOException
private StoreMetadata targetMetadata( File databaseDir ) throws IOException
{
FileSystemAbstraction fs = new DefaultFileSystemAbstraction();
File metadataStore = new File( databaseDir, MetaDataStore.DEFAULT_NAME );
try ( PageCache pageCache = StandalonePageCacheFactory.createPageCache( fs ) )
{
StoreId before = readStoreId( metadataStore, pageCache );
long lastTxId = MetaDataStore.getRecord( pageCache, metadataStore, LAST_TRANSACTION_ID );
return new TargetMetadata( before, lastTxId );
return new StoreMetadata( before, lastTxId );
}
}

private SourceMetadata changeStoreId( File storeDir, SourceMetadata conversionId ) throws IOException
private ClusterSeed changeStoreId( File storeDir, ClusterSeed conversionId ) throws IOException
{
FileSystemAbstraction fs = new DefaultFileSystemAbstraction();
File metadataStore = new File( storeDir, MetaDataStore.DEFAULT_NAME );
Expand All @@ -123,7 +124,7 @@ private SourceMetadata changeStoreId( File storeDir, SourceMetadata conversionId
MetaDataStore.setRecord( pageCache, metadataStore, UPGRADE_TIME, upgradeTime );

StoreId after = readStoreId( metadataStore, pageCache );
return new SourceMetadata( before, after, lastTxId );
return new ClusterSeed( before, after, lastTxId );
}
}

Expand Down Expand Up @@ -157,7 +158,7 @@ private void appendNullTransactionLogEntryToSetRaftIndexToMinusOne( File dbDir,
db.shutdown();
}

private void addIdAllocationState( File dbDir ) throws Throwable
private void addIdAllocationState( File dbDir ) throws IOException
{
File clusterStateDirectory = new File( dbDir, "cluster-state" );

Expand Down
Expand Up @@ -53,7 +53,7 @@ public static void main( String[] incomingArguments ) throws Throwable
String configPath = args.interpretOption( "config", Converters.<String>mandatory(), s -> s );
Config config = createConfig( homeDir, databaseName, configPath );

SourceMetadata metadata = new GenerateClusterSeedCommand().generate( config.get( database_path ) );
ClusterSeed metadata = new GenerateClusterSeedCommand().generate( config.get( database_path ) );
System.out.println( "Cluster Seed: " + metadata.getConversionId() );
}

Expand Down
Expand Up @@ -39,12 +39,12 @@

public class GenerateClusterSeedCommand
{
public SourceMetadata generate( File databaseDir ) throws Throwable
public ClusterSeed generate( File databaseDir ) throws IOException
{
return metadata( databaseDir );
}

private SourceMetadata metadata( File storeDir ) throws IOException
private ClusterSeed metadata( File storeDir ) throws IOException
{
FileSystemAbstraction fs = new DefaultFileSystemAbstraction();
File metadataStore = new File( storeDir, MetaDataStore.DEFAULT_NAME );
Expand All @@ -54,11 +54,11 @@ private SourceMetadata metadata( File storeDir ) throws IOException
StoreId after = storeId( metadataStore, pageCache, System.currentTimeMillis() );
long lastTxId = getRecord( pageCache, metadataStore, LAST_TRANSACTION_ID );

return new SourceMetadata( before, after, lastTxId );
return new ClusterSeed( before, after, lastTxId );
}
}

private StoreId storeId( File metadataStore, PageCache pageCache, long upgradeTime ) throws IOException
public static StoreId storeId( File metadataStore, PageCache pageCache, long upgradeTime ) throws IOException
{
long creationTime = getRecord( pageCache, metadataStore, TIME );
long randomNumber = getRecord( pageCache, metadataStore, RANDOM_NUMBER );
Expand Down
Expand Up @@ -23,21 +23,21 @@

import org.neo4j.kernel.impl.store.StoreId;

public class TargetMetadata
public class StoreMetadata
{
private final StoreId before;
private final StoreId storeId;
private final long lastTxId;

public TargetMetadata( StoreId before, long lastTxId )
public StoreMetadata( StoreId storeId, long lastTxId )
{
this.before = before;
this.storeId = storeId;
this.lastTxId = lastTxId;
}

@Override
public String toString()
{
return String.format( "TargetMetadata{before=%s, lastTxId=%d}", before, lastTxId );
return String.format( "TargetMetadata{before=%s, lastTxId=%d}", storeId, lastTxId );
}

@Override
Expand All @@ -51,14 +51,14 @@ public boolean equals( Object o )
{
return false;
}
TargetMetadata that = (TargetMetadata) o;
return lastTxId == that.lastTxId && storeIdEquals( before, that.before );
StoreMetadata that = (StoreMetadata) o;
return lastTxId == that.lastTxId && storeIdEquals( storeId, that.storeId );
}

@Override
public int hashCode()
{
int result = 31 + (this.before == null ? 0 : before.theRealHashCode());
int result = 31 + (this.storeId == null ? 0 : storeId.theRealHashCode());
return 31 * result + Objects.hash( lastTxId );
}

Expand All @@ -67,9 +67,9 @@ private boolean storeIdEquals( StoreId one, StoreId two)
return (one == two || (one != null && one.theRealEquals( two )));
}

public StoreId before()
public StoreId storeId()
{
return before;
return storeId;
}

public long lastTxId()
Expand Down
Expand Up @@ -80,7 +80,7 @@ public STATE getInitialState()
}

@Override
public synchronized void shutdown() throws Throwable
public synchronized void shutdown() throws IOException
{
currentStoreChannel.close();
currentStoreChannel = null;
Expand Down
@@ -0,0 +1,119 @@

/*
* Copyright (c) 2002-2016 "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.restore;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.neo4j.coreedge.convert.ConversionVerifier;
import org.neo4j.coreedge.convert.ConvertClassicStoreCommand;
import org.neo4j.dbms.DatabaseManagementSystemSettings;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.helpers.Args;
import org.neo4j.helpers.ArrayUtil;
import org.neo4j.io.fs.DefaultFileSystemAbstraction;
import org.neo4j.kernel.api.exceptions.TransactionFailureException;
import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.impl.util.Converters;
import org.neo4j.logging.NullLog;
import org.neo4j.server.configuration.ConfigLoader;

import static org.neo4j.dbms.DatabaseManagementSystemSettings.database_path;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.record_format;

public class RestoreExistingClusterCli
{
public static void main( String[] incomingArguments )
{
Args args = Args.parse( incomingArguments );
if ( ArrayUtil.isEmpty( incomingArguments ) )
{
printUsage( System.out );
System.exit( 1 );
}

File homeDir = args.interpretOption( "home-dir", Converters.<File>mandatory(), File::new );
String databaseName = args.interpretOption( "database", Converters.<String>mandatory(), s -> s );
String configPath = args.interpretOption( "config", Converters.<String>mandatory(), s -> s );
String fromPath = args.interpretOption( "from", Converters.<String>mandatory(), s -> s );
String clusterSeed = args.interpretOption( "cluster-seed", Converters.<String>mandatory(), s -> s );
boolean forceOverwrite = args.getBoolean( "force", Boolean.FALSE, true );

try
{
Config config = loadNeo4jConfig( homeDir, configPath );
restoreDatabase( databaseName, fromPath, forceOverwrite, config );
convertStore( config, clusterSeed );
}
catch ( IOException | TransactionFailureException e )
{
throw new RuntimeException( e );
}
}

private static Config loadNeo4jConfig( File homeDir, String configPath )
{
return new ConfigLoader( settings() ).loadConfig( Optional.of( homeDir ),
Optional.of( new File( configPath, "neo4j.conf" ) ), NullLog.getInstance() );
}

private static void convertStore( Config config, String seed ) throws IOException, TransactionFailureException
{
ConvertClassicStoreCommand convert = new ConvertClassicStoreCommand( new ConversionVerifier() );
convert.convert( config.get( database_path ), config.get( record_format ), seed );
}

private static void restoreDatabase( String databaseName, String fromPath, boolean forceOverwrite, Config config )
throws IOException
{
new RestoreDatabaseCommand( new DefaultFileSystemAbstraction(),
new File( fromPath ), config, databaseName, forceOverwrite ).execute();
}

public static List<Class<?>> settings()
{
List<Class<?>> settings = new ArrayList<>();
settings.add( GraphDatabaseSettings.class );
settings.add( DatabaseManagementSystemSettings.class );
return settings;
}

private static void printUsage( PrintStream out )
{
out.println( "Neo4j Restore Existing Cluster Tool" );
for ( String line : Args.splitLongLine( "The restore tool is used to restore a backed up core database", 80 ) )
{
out.println( "\t" + line );
}

out.println( "Usage:" );
out.println("--home-dir <path-to-neo4j>");
out.println("--from <path-to-backup-directory>");
out.println("--database <database-name>");
out.println("--config <path-to-config-directory>");
out.println("--seed <generated seed>");
out.println("--force");
}
}

0 comments on commit e4906a2

Please sign in to comment.