Skip to content

Commit

Permalink
Backup verifies local store id
Browse files Browse the repository at this point in the history
Backup verifies that local store id matches remote for incremental backup over catchup protocol.
Backup verifies that local store id doesn't exist for full backup over catchup protocol.
  • Loading branch information
phughk committed Apr 17, 2018
1 parent d3e9eaf commit 5ebd1d6
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 19 deletions.
Expand Up @@ -21,6 +21,7 @@


import java.util.List; import java.util.List;


import org.neo4j.causalclustering.catchup.storecopy.StoreFiles;
import org.neo4j.com.storecopy.FileMoveProvider; import org.neo4j.com.storecopy.FileMoveProvider;
import org.neo4j.commandline.admin.OutsideWorld; import org.neo4j.commandline.admin.OutsideWorld;
import org.neo4j.consistency.ConsistencyCheckService; import org.neo4j.consistency.ConsistencyCheckService;
Expand Down Expand Up @@ -70,7 +71,8 @@ BackupStrategyCoordinator backupStrategyCoordinator(
long timeout = onlineBackupContext.getRequiredArguments().getTimeout(); long timeout = onlineBackupContext.getRequiredArguments().getTimeout();
Config config = onlineBackupContext.getConfig(); Config config = onlineBackupContext.getConfig();


BackupStrategy ccStrategy = new CausalClusteringBackupStrategy( backupDelegator, addressResolver, logProvider ); StoreFiles storeFiles = new StoreFiles( fs, pageCache );
BackupStrategy ccStrategy = new CausalClusteringBackupStrategy( backupDelegator, addressResolver, logProvider, storeFiles );
BackupStrategy haStrategy = new HaBackupStrategy( backupProtocolService, addressResolver, logProvider, timeout ); BackupStrategy haStrategy = new HaBackupStrategy( backupProtocolService, addressResolver, logProvider, timeout );


BackupStrategyWrapper ccStrategyWrapper = wrap( ccStrategy, copyService, pageCache, config, recoveryService ); BackupStrategyWrapper ccStrategyWrapper = wrap( ccStrategy, copyService, pageCache, config, recoveryService );
Expand Down
Expand Up @@ -19,10 +19,14 @@
*/ */
package org.neo4j.backup.impl; package org.neo4j.backup.impl;


import java.io.File;
import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Optional;


import org.neo4j.causalclustering.catchup.CatchupResult; import org.neo4j.causalclustering.catchup.CatchupResult;
import org.neo4j.causalclustering.catchup.storecopy.StoreCopyFailedException; import org.neo4j.causalclustering.catchup.storecopy.StoreCopyFailedException;
import org.neo4j.causalclustering.catchup.storecopy.StoreFiles;
import org.neo4j.causalclustering.catchup.storecopy.StoreIdDownloadFailedException; import org.neo4j.causalclustering.catchup.storecopy.StoreIdDownloadFailedException;
import org.neo4j.causalclustering.identity.StoreId; import org.neo4j.causalclustering.identity.StoreId;
import org.neo4j.helpers.AdvertisedSocketAddress; import org.neo4j.helpers.AdvertisedSocketAddress;
Expand All @@ -32,17 +36,21 @@
import org.neo4j.logging.Log; import org.neo4j.logging.Log;
import org.neo4j.logging.LogProvider; import org.neo4j.logging.LogProvider;


import static java.lang.String.format;

class CausalClusteringBackupStrategy extends LifecycleAdapter implements BackupStrategy class CausalClusteringBackupStrategy extends LifecycleAdapter implements BackupStrategy
{ {
private final BackupDelegator backupDelegator; private final BackupDelegator backupDelegator;
private final AddressResolver addressResolver; private final AddressResolver addressResolver;
private final Log log; private final Log log;
private final StoreFiles storeFiles;


CausalClusteringBackupStrategy( BackupDelegator backupDelegator, AddressResolver addressResolver, LogProvider logProvider ) CausalClusteringBackupStrategy( BackupDelegator backupDelegator, AddressResolver addressResolver, LogProvider logProvider, StoreFiles storeFiles )
{ {
this.backupDelegator = backupDelegator; this.backupDelegator = backupDelegator;
this.addressResolver = addressResolver; this.addressResolver = addressResolver;
this.log = logProvider.getLog( CausalClusteringBackupStrategy.class ); this.log = logProvider.getLog( CausalClusteringBackupStrategy.class );
this.storeFiles = storeFiles;
} }


@Override @Override
Expand All @@ -62,6 +70,13 @@ public Fallible<BackupStageOutcome> performFullBackup( Path desiredBackupLocatio
return new Fallible<>( BackupStageOutcome.WRONG_PROTOCOL, e ); return new Fallible<>( BackupStageOutcome.WRONG_PROTOCOL, e );
} }


Optional<StoreId> expectedStoreId = readLocalStoreId( desiredBackupLocation.toFile() );
if ( expectedStoreId.isPresent() )
{
return new Fallible<>( BackupStageOutcome.FAILURE, new StoreIdDownloadFailedException(
format( "Cannot perform a full backup onto preexisting backup. Remote store id was %s but local is %s", storeId, expectedStoreId ) ) );
}

try try
{ {
backupDelegator.copy( fromAddress, storeId, desiredBackupLocation ); backupDelegator.copy( fromAddress, storeId, desiredBackupLocation );
Expand Down Expand Up @@ -89,6 +104,12 @@ public Fallible<BackupStageOutcome> performIncrementalBackup( Path desiredBackup
{ {
return new Fallible<>( BackupStageOutcome.WRONG_PROTOCOL, e ); return new Fallible<>( BackupStageOutcome.WRONG_PROTOCOL, e );
} }
Optional<StoreId> expectedStoreId = readLocalStoreId( desiredBackupLocation.toFile() );
if ( !expectedStoreId.isPresent() || !expectedStoreId.get().equals( storeId ) )
{
return new Fallible<>( BackupStageOutcome.FAILURE,
new StoreIdDownloadFailedException( format( "Remote store id was %s but local is %s", storeId, expectedStoreId ) ) );
}
return catchup( fromAddress, storeId, desiredBackupLocation ); return catchup( fromAddress, storeId, desiredBackupLocation );
} }


Expand All @@ -106,6 +127,18 @@ public void stop() throws Throwable
super.stop(); super.stop();
} }


private Optional<StoreId> readLocalStoreId( File backupLocation )
{
try
{
return Optional.of( storeFiles.readStoreId( backupLocation ) );
}
catch ( IOException e )
{
return Optional.empty();
}
}

private Fallible<BackupStageOutcome> catchup( AdvertisedSocketAddress fromAddress, StoreId storeId, Path backupTarget ) private Fallible<BackupStageOutcome> catchup( AdvertisedSocketAddress fromAddress, StoreId storeId, Path backupTarget )
{ {
CatchupResult catchupResult; CatchupResult catchupResult;
Expand Down
Expand Up @@ -24,10 +24,12 @@
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;


import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;


import org.neo4j.causalclustering.catchup.CatchupResult; import org.neo4j.causalclustering.catchup.CatchupResult;
import org.neo4j.causalclustering.catchup.storecopy.StoreCopyFailedException; import org.neo4j.causalclustering.catchup.storecopy.StoreCopyFailedException;
import org.neo4j.causalclustering.catchup.storecopy.StoreFiles;
import org.neo4j.causalclustering.catchup.storecopy.StoreIdDownloadFailedException; import org.neo4j.causalclustering.catchup.storecopy.StoreIdDownloadFailedException;
import org.neo4j.causalclustering.identity.StoreId; import org.neo4j.causalclustering.identity.StoreId;
import org.neo4j.helpers.AdvertisedSocketAddress; import org.neo4j.helpers.AdvertisedSocketAddress;
Expand Down Expand Up @@ -58,12 +60,16 @@ public class CausalClusteringBackupStrategyTest
Path desiredBackupLocation = mock( Path.class ); Path desiredBackupLocation = mock( Path.class );
Config config = mock( Config.class ); Config config = mock( Config.class );
OptionalHostnamePort userProvidedAddress = new OptionalHostnamePort( (String) null, null, null ); OptionalHostnamePort userProvidedAddress = new OptionalHostnamePort( (String) null, null, null );
StoreFiles storeFiles = mock( StoreFiles.class );
StoreId expectedStoreId = new StoreId( 11, 22, 33, 44 );


@Before @Before
public void setup() public void setup() throws IOException, StoreIdDownloadFailedException
{ {
when( addressResolver.resolveCorrectCCAddress( any(), any() ) ).thenReturn( resolvedFromAddress ); when( addressResolver.resolveCorrectCCAddress( any(), any() ) ).thenReturn( resolvedFromAddress );
subject = new CausalClusteringBackupStrategy( backupDelegator, addressResolver, NullLogProvider.getInstance() ); when( storeFiles.readStoreId( any() ) ).thenReturn( expectedStoreId );
when( backupDelegator.fetchStoreId( any() ) ).thenReturn( expectedStoreId );
subject = new CausalClusteringBackupStrategy( backupDelegator, addressResolver, NullLogProvider.getInstance(), storeFiles );
} }


@Test @Test
Expand Down Expand Up @@ -97,32 +103,27 @@ public void fullBackupUsesCorrectResolvedAddress() throws StoreIdDownloadFailedE
@Test @Test
public void incrementalRunsCatchupWithTargetsStoreId() throws StoreIdDownloadFailedException, StoreCopyFailedException public void incrementalRunsCatchupWithTargetsStoreId() throws StoreIdDownloadFailedException, StoreCopyFailedException
{ {
// given
StoreId storeId = anyStoreId();

when( backupDelegator.fetchStoreId( resolvedFromAddress ) ).thenReturn( storeId );


// when // when
subject.performIncrementalBackup( desiredBackupLocation, config, userProvidedAddress ); subject.performIncrementalBackup( desiredBackupLocation, config, userProvidedAddress );


// then // then
verify( backupDelegator ).fetchStoreId( resolvedFromAddress ); verify( backupDelegator ).fetchStoreId( resolvedFromAddress );
verify( backupDelegator ).tryCatchingUp( eq( resolvedFromAddress ), eq( storeId ), any() ); verify( backupDelegator ).tryCatchingUp( eq( resolvedFromAddress ), eq( expectedStoreId ), any() );
} }


@Test @Test
public void fullRunsRetrieveStoreWithTargetsStoreId() throws StoreIdDownloadFailedException, StoreCopyFailedException public void fullRunsRetrieveStoreWithTargetsStoreId() throws StoreIdDownloadFailedException, StoreCopyFailedException, IOException
{ {
// given // given
StoreId storeId = anyStoreId(); when( storeFiles.readStoreId( any() ) ).thenThrow( IOException.class );
when( backupDelegator.fetchStoreId( resolvedFromAddress ) ).thenReturn( storeId );


// when // when
subject.performFullBackup( desiredBackupLocation, config, userProvidedAddress ); subject.performFullBackup( desiredBackupLocation, config, userProvidedAddress );


// then // then
verify( backupDelegator ).fetchStoreId( resolvedFromAddress ); verify( backupDelegator ).fetchStoreId( resolvedFromAddress );
verify( backupDelegator ).copy( resolvedFromAddress, storeId, desiredBackupLocation ); verify( backupDelegator ).copy( resolvedFromAddress, expectedStoreId, desiredBackupLocation );
} }


@Test @Test
Expand All @@ -145,9 +146,7 @@ public void failingToRetrieveStoreIdCausesFailWithStatus_incrementalBackup() thr
public void failingToCopyStoresCausesFailWithStatus_incrementalBackup() throws StoreIdDownloadFailedException, StoreCopyFailedException public void failingToCopyStoresCausesFailWithStatus_incrementalBackup() throws StoreIdDownloadFailedException, StoreCopyFailedException
{ {
// given // given
StoreId storeId = anyStoreId(); when( backupDelegator.tryCatchingUp( any(), eq( expectedStoreId ), any() ) ).thenThrow( StoreCopyFailedException.class );
when( backupDelegator.fetchStoreId( any() ) ).thenReturn( storeId );
when( backupDelegator.tryCatchingUp( any(), eq( storeId ), any() ) ).thenThrow( StoreCopyFailedException.class );


// when // when
Fallible state = subject.performIncrementalBackup( desiredBackupLocation, config, userProvidedAddress ); Fallible state = subject.performIncrementalBackup( desiredBackupLocation, config, userProvidedAddress );
Expand All @@ -173,16 +172,20 @@ public void failingToRetrieveStoreIdCausesFailWithStatus_fullBackup() throws Sto
} }


@Test @Test
public void failingToCopyStoresCausesFailWithStatus_fullBackup() throws StoreCopyFailedException public void failingToCopyStoresCausesFailWithStatus_fullBackup() throws StoreCopyFailedException, IOException
{ {
// given // given
doThrow( StoreCopyFailedException.class ).when( backupDelegator ).copy( any(), any(), any() ); doThrow( StoreCopyFailedException.class ).when( backupDelegator ).copy( any(), any(), any() );


// and
when( storeFiles.readStoreId( any() ) ).thenThrow( IOException.class );

// when // when
Fallible state = subject.performFullBackup( desiredBackupLocation, config, userProvidedAddress ); Fallible state = subject.performFullBackup( desiredBackupLocation, config, userProvidedAddress );


// then // then
assertEquals( BackupStageOutcome.FAILURE, state.getState() ); assertEquals( BackupStageOutcome.FAILURE, state.getState() );
System.out.println( state.getCause() );
assertEquals( StoreCopyFailedException.class, state.getCause().get().getClass() ); assertEquals( StoreCopyFailedException.class, state.getCause().get().getClass() );
} }


Expand Down Expand Up @@ -220,8 +223,45 @@ public void lifecycleDelegatesToNecessaryServices() throws Throwable
verify( backupDelegator ).stop(); verify( backupDelegator ).stop();
} }


private StoreId anyStoreId() @Test
public void exceptionWhenStoreMismatchNoExistingBackup() throws IOException
{
// given
when( storeFiles.readStoreId( any() ) ).thenThrow( IOException.class );

// when
Fallible<BackupStageOutcome> state = subject.performIncrementalBackup( desiredBackupLocation, config, userProvidedAddress );

// then
assertEquals( StoreIdDownloadFailedException.class, state.getCause().get().getClass() );
assertEquals( BackupStageOutcome.FAILURE, state.getState() );
}

@Test
public void exceptionWhenStoreMismatch() throws IOException
{
// given
when( storeFiles.readStoreId( any() ) ).thenReturn( new StoreId( 5, 4, 3, 2 ) );

// when
Fallible<BackupStageOutcome> state = subject.performIncrementalBackup( desiredBackupLocation, config, userProvidedAddress );

// then
assertEquals( StoreIdDownloadFailedException.class, state.getCause().get().getClass() );
assertEquals( BackupStageOutcome.FAILURE, state.getState() );
}

@Test
public void fullBackupFailsWhenTargetHasStoreId() throws IOException
{ {
return new StoreId( 1, 2, 3, 4 ); // given
when( storeFiles.readStoreId( any() ) ).thenReturn( expectedStoreId );

// when
Fallible<BackupStageOutcome> state = subject.performFullBackup( desiredBackupLocation, config, userProvidedAddress );

// then
assertEquals( StoreIdDownloadFailedException.class, state.getCause().get().getClass() );
assertEquals( BackupStageOutcome.FAILURE, state.getState() );
} }
} }

0 comments on commit 5ebd1d6

Please sign in to comment.