Skip to content

Commit

Permalink
Add recovery on incremental backups
Browse files Browse the repository at this point in the history
All backups should provide a recovered store as output.
  • Loading branch information
RagnarW committed Oct 12, 2018
1 parent aa4ec13 commit f079b50
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 10 deletions.
Expand Up @@ -77,7 +77,6 @@ Fallible<BackupStrategyOutcome> doBackup( OnlineBackupContext onlineBackupContex
private Fallible<BackupStrategyOutcome> performBackupWithoutLifecycle( OnlineBackupContext onlineBackupContext ) private Fallible<BackupStrategyOutcome> performBackupWithoutLifecycle( OnlineBackupContext onlineBackupContext )
{ {
Path backupLocation = onlineBackupContext.getResolvedLocationFromName(); Path backupLocation = onlineBackupContext.getResolvedLocationFromName();
Path userSpecifiedBackupLocation = onlineBackupContext.getResolvedLocationFromName();
OptionalHostnamePort userSpecifiedAddress = onlineBackupContext.getRequiredArguments().getAddress(); OptionalHostnamePort userSpecifiedAddress = onlineBackupContext.getRequiredArguments().getAddress();
log.debug( "User specified address is %s:%s", userSpecifiedAddress.getHostname().toString(), userSpecifiedAddress.getPort().toString() ); log.debug( "User specified address is %s:%s", userSpecifiedAddress.getHostname().toString(), userSpecifiedAddress.getPort().toString() );
Config config = onlineBackupContext.getConfig(); Config config = onlineBackupContext.getConfig();
Expand All @@ -86,10 +85,13 @@ private Fallible<BackupStrategyOutcome> performBackupWithoutLifecycle( OnlineBac
if ( previousBackupExists ) if ( previousBackupExists )
{ {
log.info( "Previous backup found, trying incremental backup." ); log.info( "Previous backup found, trying incremental backup." );
Fallible<BackupStageOutcome> state = Fallible<BackupStageOutcome> state = backupStrategy.performIncrementalBackup( backupLocation, config, userSpecifiedAddress );
backupStrategy.performIncrementalBackup( userSpecifiedBackupLocation, config, userSpecifiedAddress );
boolean fullBackupWontWork = BackupStageOutcome.WRONG_PROTOCOL.equals( state.getState() ); boolean fullBackupWontWork = BackupStageOutcome.WRONG_PROTOCOL.equals( state.getState() );
boolean incrementalWasSuccessful = BackupStageOutcome.SUCCESS.equals( state.getState() ); boolean incrementalWasSuccessful = BackupStageOutcome.SUCCESS.equals( state.getState() );
if ( incrementalWasSuccessful )
{
backupRecoveryService.recoverWithDatabase( backupLocation, pageCache, config );
}


if ( fullBackupWontWork || incrementalWasSuccessful ) if ( fullBackupWontWork || incrementalWasSuccessful )
{ {
Expand Down
Expand Up @@ -317,7 +317,7 @@ public void performingFullBackupInvokesRecovery()
} }


@Test @Test
public void performingIncrementalBackupDoesNotInvokeRecovery() public void performingIncrementalBackupInvokesRecovery()
{ {
// given backup exists // given backup exists
when( backupCopyService.backupExists( any() ) ).thenReturn( true ); when( backupCopyService.backupExists( any() ) ).thenReturn( true );
Expand All @@ -331,7 +331,7 @@ public void performingIncrementalBackupDoesNotInvokeRecovery()
subject.doBackup( onlineBackupContext ); subject.doBackup( onlineBackupContext );


// then // then
verify( backupRecoveryService, never() ).recoverWithDatabase( any(), any(), any() ); verify( backupRecoveryService ).recoverWithDatabase( any(), any(), any() );
} }


@Test @Test
Expand Down
Expand Up @@ -43,6 +43,7 @@
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.IntFunction; import java.util.function.IntFunction;
Expand All @@ -58,21 +59,29 @@
import org.neo4j.causalclustering.discovery.IpFamily; import org.neo4j.causalclustering.discovery.IpFamily;
import org.neo4j.causalclustering.discovery.SharedDiscoveryServiceFactory; import org.neo4j.causalclustering.discovery.SharedDiscoveryServiceFactory;
import org.neo4j.causalclustering.helpers.CausalClusteringTestHelpers; import org.neo4j.causalclustering.helpers.CausalClusteringTestHelpers;
import org.neo4j.consistency.ConsistencyCheckService;
import org.neo4j.consistency.checking.full.ConsistencyFlags;
import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.helpers.progress.ProgressMonitorFactory;
import org.neo4j.io.ByteUnit; import org.neo4j.io.ByteUnit;
import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.configuration.Settings; import org.neo4j.kernel.configuration.Settings;
import org.neo4j.kernel.impl.enterprise.configuration.OnlineBackupSettings; import org.neo4j.kernel.impl.enterprise.configuration.OnlineBackupSettings;
import org.neo4j.kernel.impl.recovery.RecoveryRequiredChecker;
import org.neo4j.kernel.impl.store.format.highlimit.HighLimit; import org.neo4j.kernel.impl.store.format.highlimit.HighLimit;
import org.neo4j.kernel.impl.store.format.standard.Standard; import org.neo4j.kernel.impl.store.format.standard.Standard;
import org.neo4j.kernel.impl.transaction.log.files.LogFiles; import org.neo4j.kernel.impl.transaction.log.files.LogFiles;
import org.neo4j.kernel.monitoring.Monitors;
import org.neo4j.logging.NullLogProvider;
import org.neo4j.ports.allocation.PortAuthority; import org.neo4j.ports.allocation.PortAuthority;
import org.neo4j.test.DbRepresentation; import org.neo4j.test.DbRepresentation;
import org.neo4j.test.causalclustering.ClusterRule; import org.neo4j.test.causalclustering.ClusterRule;
import org.neo4j.test.rule.PageCacheRule;
import org.neo4j.test.rule.SuppressOutput; import org.neo4j.test.rule.SuppressOutput;
import org.neo4j.test.rule.TestDirectory; import org.neo4j.test.rule.TestDirectory;
import org.neo4j.test.rule.fs.DefaultFileSystemRule;
import org.neo4j.util.TestHelpers; import org.neo4j.util.TestHelpers;


import static java.lang.String.format; import static java.lang.String.format;
Expand All @@ -84,12 +93,17 @@
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeFalse;
import static org.neo4j.backup.impl.OnlineBackupContextBuilder.ARG_NAME_FALLBACK_FULL;


@RunWith( Parameterized.class ) @RunWith( Parameterized.class )
public class OnlineBackupCommandCcIT public class OnlineBackupCommandCcIT
{ {
@Rule @Rule
public final TestDirectory testDirectory = TestDirectory.testDirectory(); public final DefaultFileSystemRule fileSystemRule = new DefaultFileSystemRule();
@Rule
public final TestDirectory testDirectory = TestDirectory.testDirectory( fileSystemRule );
@Rule
public final PageCacheRule pageCacheRule = new PageCacheRule();


@Rule @Rule
public ClusterRule clusterRule = new ClusterRule() public ClusterRule clusterRule = new ClusterRule()
Expand Down Expand Up @@ -192,7 +206,8 @@ public void backupCanBeOptionallySwitchedOnWithTheBackupConfig() throws Exceptio
// and an incremental backup is performed // and an incremental backup is performed
createSomeData( cluster ); createSomeData( cluster );
assertEquals( 0, runBackupToolFromOtherJvmToGetExitCode( "--from=" + customAddress, "--cc-report-dir=" + backupDir, "--backup-dir=" + backupDir, assertEquals( 0, runBackupToolFromOtherJvmToGetExitCode( "--from=" + customAddress, "--cc-report-dir=" + backupDir, "--backup-dir=" + backupDir,
"--name=defaultport" ) ); "--name=defaultport",
arg(ARG_NAME_FALLBACK_FULL, false) ) );


// then the data matches // then the data matches
assertEquals( DbRepresentation.of( clusterDatabase( cluster ) ), getBackupDbRepresentation( "defaultport", backupDir ) ); assertEquals( DbRepresentation.of( clusterDatabase( cluster ) ), getBackupDbRepresentation( "defaultport", backupDir ) );
Expand Down Expand Up @@ -285,6 +300,64 @@ public void reportsProgress() throws Exception
assertTrue( output.contains( "Finished receiving index snapshots" ) ); assertTrue( output.contains( "Finished receiving index snapshots" ) );
} }


@Test
public void fullBackupIsRecoveredAndConsistent() throws Exception
{
// given database exists with data
Cluster cluster = startCluster( recordFormat );
createSomeData( cluster );
String address = TestHelpers.backupAddressCc( clusterLeader( cluster ).database() );

String name = UUID.randomUUID().toString();
File backupLocation = new File( backupDir, name );

// when
assertEquals( 0,
runBackupToolFromOtherJvmToGetExitCode( "--from", address, "--cc-report-dir=" + backupDir, "--backup-dir=" + backupDir, "--name=" + name ) );

// then
assertFalse( "Store should not require recovery",
new RecoveryRequiredChecker( fileSystemRule, pageCacheRule.getPageCache( fileSystemRule ), Config.defaults(),
new Monitors() ).isRecoveryRequiredAt( backupLocation ) );
ConsistencyFlags consistencyFlags = new ConsistencyFlags( true, true, true, true );
assertTrue( "Consistency check failed", new ConsistencyCheckService()
.runFullConsistencyCheck( backupLocation, Config.defaults(), ProgressMonitorFactory.NONE, NullLogProvider.getInstance(), false,
consistencyFlags )
.isSuccessful() );
}

@Test
public void incrementalBackupIsRecoveredAndConsistent() throws Exception
{
// given database exists with data
Cluster cluster = startCluster( recordFormat );
createSomeData( cluster );
String address = TestHelpers.backupAddressCc( clusterLeader( cluster ).database() );

String name = UUID.randomUUID().toString();
File backupLocation = new File( backupDir, name );

// when
assertEquals( 0,
runBackupToolFromOtherJvmToGetExitCode( "--from", address, "--cc-report-dir=" + backupDir, "--backup-dir=" + backupDir, "--name=" + name ) );

// and
createSomeData( cluster );
assertEquals( 0,
runBackupToolFromOtherJvmToGetExitCode( "--from", address, "--cc-report-dir=" + backupDir, "--backup-dir=" + backupDir, "--name=" + name,
arg( ARG_NAME_FALLBACK_FULL, false ) ) );

// then
assertFalse( "Store should not require recovery",
new RecoveryRequiredChecker( fileSystemRule, pageCacheRule.getPageCache( fileSystemRule ), Config.defaults(),
new Monitors() ).isRecoveryRequiredAt( backupLocation ) );
ConsistencyFlags consistencyFlags = new ConsistencyFlags( true, true, true, true );
assertTrue( "Consistency check failed", new ConsistencyCheckService()
.runFullConsistencyCheck( backupLocation, Config.defaults(), ProgressMonitorFactory.NONE, NullLogProvider.getInstance(), false,
consistencyFlags )
.isSuccessful() );
}

@Test @Test
public void onlyTheLatestTransactionIsKeptAfterIncrementalBackup() throws Exception public void onlyTheLatestTransactionIsKeptAfterIncrementalBackup() throws Exception
{ {
Expand Down Expand Up @@ -320,7 +393,8 @@ public void onlyTheLatestTransactionIsKeptAfterIncrementalBackup() throws Except
"--cc-report-dir=" + backupDir, "--cc-report-dir=" + backupDir,
"--backup-dir=" + backupDir, "--backup-dir=" + backupDir,
"--additional-config=" + configOverrideFile, "--additional-config=" + configOverrideFile,
"--name=" + backupName ) ); "--name=" + backupName,
arg(ARG_NAME_FALLBACK_FULL, false) ) );


// then there has been a rotation // then there has been a rotation
BackupTransactionLogFilesHelper backupTransactionLogFilesHelper = new BackupTransactionLogFilesHelper(); BackupTransactionLogFilesHelper backupTransactionLogFilesHelper = new BackupTransactionLogFilesHelper();
Expand Down Expand Up @@ -361,8 +435,8 @@ public void ipv6Enabled() throws Exception
"--from", customAddress, "--from", customAddress,
"--protocol=catchup", "--protocol=catchup",
"--cc-report-dir=" + backupDir, "--cc-report-dir=" + backupDir,
"--backup-dir=" + backupDir, "--name=" + backupName, "--backup-dir=" + backupDir,
"--name=" + backupName ) ); arg(ARG_NAME_FALLBACK_FULL, false) ) );


// then // then
assertEquals( DbRepresentation.of( clusterDatabase( cluster ) ), getBackupDbRepresentation( backupName, backupDir ) ); assertEquals( DbRepresentation.of( clusterDatabase( cluster ) ), getBackupDbRepresentation( backupName, backupDir ) );
Expand Down Expand Up @@ -412,6 +486,11 @@ public void backupRenamesWork() throws Exception
assertNotEquals( firstDatabaseRepresentation, secondDatabaseRepresentation ); assertNotEquals( firstDatabaseRepresentation, secondDatabaseRepresentation );
} }


private String arg( String key, Object value )
{
return "--" + key + "=" + value;
}

static PrintStream wrapWithNormalOutput( PrintStream normalOutput, PrintStream nullAbleOutput ) static PrintStream wrapWithNormalOutput( PrintStream normalOutput, PrintStream nullAbleOutput )
{ {
if ( nullAbleOutput == null ) if ( nullAbleOutput == null )
Expand Down

0 comments on commit f079b50

Please sign in to comment.