From 006dce8d44ea1c4853eb0696c2a759194ee2d617 Mon Sep 17 00:00:00 2001 From: MishaDemianenko Date: Thu, 13 Oct 2016 00:14:05 +0200 Subject: [PATCH] Specific hanling of upgrade transaction during update pooling During store upgrade in case if transaction logs are missing upgrade transaction checksum will be set to predefined constant value (since we can't get find real value in the logs). In HA setup upgraded store will be copied by slaves and update pullers will try to fetch updates starting from upgrade transaction id. Master should be able to recognise that specific upgrade transaction id and use checksum that was assign to it during upgrade. --- .../storemigration/legacylogs/LegacyLogs.java | 12 ++-- .../participant/StoreMigrator.java | 51 +++++++------ .../legacylogs/LegacyLogsTest.java | 39 ++++++++-- .../participant/StoreMigratorIT.java | 3 + .../participant/StoreMigratorTest.java | 52 ++++++++++---- .../test/java/upgrade/StoreUpgraderTest.java | 71 +++++++++++++++++- .../kernel/ha/TransactionChecksumLookup.java | 21 +++--- .../ha/cluster/DefaultMasterImplSPI.java | 3 - .../ha/TransactionChecksumLookupTest.java | 72 +++++++++++++++++++ 9 files changed, 262 insertions(+), 62 deletions(-) create mode 100644 enterprise/ha/src/test/java/org/neo4j/kernel/ha/TransactionChecksumLookupTest.java diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/storemigration/legacylogs/LegacyLogs.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/storemigration/legacylogs/LegacyLogs.java index 6cf386cc319b3..bee3c9ad8e5d8 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/storemigration/legacylogs/LegacyLogs.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/storemigration/legacylogs/LegacyLogs.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Optional; import org.neo4j.cursor.IOCursor; import org.neo4j.helpers.collection.Pair; @@ -33,7 +34,6 @@ import org.neo4j.kernel.impl.storemigration.ExistingTargetStrategy; import org.neo4j.kernel.impl.storemigration.FileOperation; import org.neo4j.kernel.impl.transaction.log.LogVersionedStoreChannel; -import org.neo4j.kernel.impl.transaction.log.NoSuchTransactionException; import org.neo4j.kernel.impl.transaction.log.entry.LogEntry; import org.neo4j.kernel.impl.transaction.log.entry.LogEntryCommit; import org.neo4j.kernel.impl.transaction.log.entry.LogEntryStart; @@ -118,10 +118,10 @@ public void migrateLogs( File storeDir, File migrationDir ) throws IOException } } - public TransactionId getTransactionInformation( File storeDir, long transactionId ) throws IOException + public Optional getTransactionInformation( File storeDir, long transactionId ) throws IOException { List logFiles = Arrays.asList( fs.listFiles( storeDir, versionedLegacyLogFilesFilter ) ); - Collections.sort( logFiles, NEWEST_FIRST ); + logFiles.sort( NEWEST_FIRST ); for ( File file : logFiles ) { Pair> pair = reader.openReadableChannel( file ); @@ -143,8 +143,8 @@ else if ( logEntry instanceof LogEntryCommit ) LogEntryCommit commitEntry = logEntry.as(); if ( commitEntry.getTxId() == transactionId ) { - return new TransactionId( transactionId, startEntry.checksum(), - commitEntry.getTimeWritten() ); + return Optional.of( new TransactionId( transactionId, startEntry.checksum(), + commitEntry.getTimeWritten() ) ); } } } @@ -155,7 +155,7 @@ else if ( logEntry instanceof LogEntryCommit ) break; } } - throw new NoSuchTransactionException( transactionId ); + return Optional.empty(); } public void operate( FileOperation op, File from, File to ) throws IOException diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigrator.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigrator.java index bd64c79f6e6b4..3e9606b9cc37f 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigrator.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigrator.java @@ -33,7 +33,9 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.function.BiConsumer; +import java.util.function.Supplier; import org.neo4j.helpers.collection.Iterables; import org.neo4j.io.fs.FileSystemAbstraction; @@ -78,7 +80,6 @@ import org.neo4j.kernel.impl.transaction.log.PhysicalLogFiles; import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; import org.neo4j.kernel.lifecycle.Lifespan; -import org.neo4j.logging.Log; import org.neo4j.logging.NullLogProvider; import org.neo4j.unsafe.impl.batchimport.AdditionalInitialIds; import org.neo4j.unsafe.impl.batchimport.BatchImporter; @@ -136,7 +137,6 @@ public class StoreMigrator extends AbstractStoreMigrationParticipant private final FileSystemAbstraction fileSystem; private final PageCache pageCache; private final SchemaIndexProvider schemaIndexProvider; - private final Log log; public StoreMigrator( FileSystemAbstraction fileSystem, PageCache pageCache, Config config, LogService logService, SchemaIndexProvider schemaIndexProvider ) @@ -154,7 +154,6 @@ public StoreMigrator( FileSystemAbstraction fileSystem, PageCache pageCache, Con this.logService = logService; this.schemaIndexProvider = schemaIndexProvider; this.legacyLogs = legacyLogs; - this.log = logService.getInternalLog( StoreMigrator.class ); } @Override @@ -278,8 +277,7 @@ private static File lastTxLogPositionFile( File migrationDir ) return new File( migrationDir, "lastxlogposition" ); } - // accessible for tests - TransactionId extractTransactionIdInformation( File neoStore, File storeDir, long txId ) + TransactionId extractTransactionIdInformation( File neoStore, File storeDir, long lastTransactionId ) throws IOException { long checksum = MetaDataStore.getRecord( pageCache, neoStore, Position.LAST_TRANSACTION_CHECKSUM ); @@ -287,24 +285,35 @@ TransactionId extractTransactionIdInformation( File neoStore, File storeDir, lon Position.LAST_TRANSACTION_COMMIT_TIMESTAMP ); if ( checksum != FIELD_NOT_PRESENT && commitTimestamp != FIELD_NOT_PRESENT ) { - return new TransactionId( txId, checksum, commitTimestamp ); + return new TransactionId( lastTransactionId, checksum, commitTimestamp ); } // The legacy store we're migrating doesn't have this record in neostore so try to extract it from tx log - try - { - return legacyLogs.getTransactionInformation( storeDir, txId ); - } - catch ( IOException ioe ) - { - log.error( "Extraction of transaction " + txId + " from legacy logs failed.", ioe ); - // OK, so we could not get the transaction information from the legacy store logs, - // so just generate a random new one. I don't think it matters since we know that in a - // multi-database scenario there can only be one of them upgrading, the other ones will have to - // copy that database. - return txId == TransactionIdStore.BASE_TX_ID - ? new TransactionId( txId, BASE_TX_CHECKSUM, BASE_TX_COMMIT_TIMESTAMP ) - : new TransactionId( txId, UNKNOWN_TX_CHECKSUM, UNKNOWN_TX_COMMIT_TIMESTAMP ); - } + + Optional transactionInformation = legacyLogs.getTransactionInformation( storeDir, lastTransactionId ); + return transactionInformation.orElseGet( specificTransactionInformationSupplier( lastTransactionId ) ); + } + + /** + * In case if we can't find information about transaction in legacy logs we will create new transaction + * information record. + * Those should be used only in case if we do not have any transaction logs available during + * migration. + * + * Logs can be absent in two possible scenarios: + *
    + *
  1. We do not have any logs since there were not transaction.
  2. + *
  3. Logs are missing.
  4. + *
+ * For both of those cases specific informational records will be produced. + * + * @param lastTransactionId last committed transaction id + * @return supplier of custom id records. + */ + private Supplier specificTransactionInformationSupplier( long lastTransactionId ) + { + return () -> lastTransactionId == TransactionIdStore.BASE_TX_ID + ? new TransactionId( lastTransactionId, BASE_TX_CHECKSUM, BASE_TX_COMMIT_TIMESTAMP ) + : new TransactionId( lastTransactionId, UNKNOWN_TX_CHECKSUM, UNKNOWN_TX_COMMIT_TIMESTAMP ); } private LogPosition extractTransactionLogPosition( File neoStore, File storeDir, long lastTxId ) throws IOException diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/legacylogs/LegacyLogsTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/legacylogs/LegacyLogsTest.java index 415147402754d..838fef2e38ddd 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/legacylogs/LegacyLogsTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/legacylogs/LegacyLogsTest.java @@ -43,6 +43,7 @@ import org.neo4j.kernel.impl.transaction.log.ArrayIOCursor; import org.neo4j.kernel.impl.transaction.log.LogPosition; import org.neo4j.kernel.impl.transaction.log.LogVersionedStoreChannel; +import org.neo4j.kernel.impl.transaction.log.MissingLogDataException; import org.neo4j.kernel.impl.transaction.log.entry.LogEntry; import org.neo4j.kernel.impl.transaction.log.entry.LogEntryCommand; import org.neo4j.kernel.impl.transaction.log.entry.LogEntryStart; @@ -50,12 +51,12 @@ import org.neo4j.kernel.impl.transaction.log.entry.OnePhaseCommit; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.neo4j.kernel.impl.store.record.Record.NO_LABELS_FIELD; import static org.neo4j.kernel.impl.storemigration.legacylogs.LegacyLogFilenames.getLegacyLogFilename; import static org.neo4j.kernel.impl.storemigration.legacylogs.LegacyLogFilenames.versionedLegacyLogFilesFilter; import static org.neo4j.kernel.impl.transaction.log.PhysicalLogFile.DEFAULT_NAME; @@ -223,9 +224,39 @@ public void transactionInformationRetrievedFromCommitEntries() throws IOExceptio LegacyLogEntryWriter writer = new LegacyLogEntryWriter( fs ); LegacyLogs legacyLogs = new LegacyLogs( fs, reader, writer ); - assertEquals( newTransactionId( 1 ), legacyLogs.getTransactionInformation( storeDir, 1 ) ); - assertEquals( newTransactionId( 2 ), legacyLogs.getTransactionInformation( storeDir, 2 ) ); - assertEquals( newTransactionId( 3 ), legacyLogs.getTransactionInformation( storeDir, 3 ) ); + assertEquals( newTransactionId( 1 ), getTransactionInformation( legacyLogs, 1 ) ); + assertEquals( newTransactionId( 2 ), getTransactionInformation( legacyLogs, 2 ) ); + assertEquals( newTransactionId( 3 ), getTransactionInformation( legacyLogs, 3 ) ); + } + + @Test(expected = IOException.class) + @SuppressWarnings( "unchecked" ) + public void ioExceptionsPropagatedWhenFailToReadLegacyLog() throws IOException + { + File logFile = new File( LegacyLogFilenames.getLegacyLogFilename( 1 ) ); + when( fs.listFiles( any( File.class ), any( FilenameFilter.class ) ) ) + .thenReturn( new File[]{logFile} ); + + when( reader.openReadableChannel( any( File.class ) ) ).thenThrow( IOException.class ); + + LegacyLogs legacyLogs = new LegacyLogs( fs, reader, writer ); + getTransactionInformation( legacyLogs, 1 ); + } + + @Test + public void noTransactionalInformationWhenLogsNotPresent() throws IOException + { + when( fs.listFiles( any( File.class ), any( FilenameFilter.class ) ) ) + .thenReturn( new File[]{} ); + + LegacyLogs legacyLogs = new LegacyLogs( fs, reader, writer ); + assertFalse( "There are not logs. Nothing to return", + legacyLogs.getTransactionInformation( storeDir, 1 ).isPresent() ); + } + + private TransactionId getTransactionInformation( LegacyLogs legacyLogs, int transactionId ) throws IOException + { + return legacyLogs.getTransactionInformation( storeDir, transactionId ).orElseThrow( MissingLogDataException::new); } private String getLogFilenameForVersion( int version ) diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigratorIT.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigratorIT.java index a63310d5a450f..fbf6fb6859084 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigratorIT.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigratorIT.java @@ -25,12 +25,15 @@ import org.junit.runners.Parameterized; import java.io.File; +import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.function.Function; +import org.neo4j.graphdb.mockfs.DelegatingFileSystemAbstraction; import org.neo4j.io.fs.DefaultFileSystemAbstraction; import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.fs.StoreChannel; import org.neo4j.io.pagecache.PageCache; import org.neo4j.kernel.api.index.SchemaIndexProvider; import org.neo4j.kernel.configuration.Config; diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigratorTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigratorTest.java index 58fe664747f34..de3c21cd31497 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigratorTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigratorTest.java @@ -25,6 +25,7 @@ import java.io.File; import java.io.IOException; +import java.util.Optional; import org.neo4j.io.fs.DefaultFileSystemAbstraction; import org.neo4j.io.fs.FileSystemAbstraction; @@ -127,7 +128,7 @@ public void shouldExtractTransactionInformationFromLegacyLogsWhenCantFindInStore Config config = mock( Config.class ); LogService logService = mock( LogService.class ); LegacyLogs legacyLogs = mock( LegacyLogs.class ); - when( legacyLogs.getTransactionInformation( storeDir, txId ) ).thenReturn( expected ); + when( legacyLogs.getTransactionInformation( storeDir, txId ) ).thenReturn( Optional.of( expected ) ); // when // ... neoStore is empty and with migrator @@ -140,23 +141,16 @@ public void shouldExtractTransactionInformationFromLegacyLogsWhenCantFindInStore } @Test - public void shouldGenerateTransactionInformationAsLastOption() throws Exception + public void shouldGenerateTransactionInformationWhenLogsNotPresent() throws Exception { // given - // ... variables long txId = 42; - TransactionId expected = new TransactionId( txId, FIELD_NOT_PRESENT, UNKNOWN_TX_COMMIT_TIMESTAMP ); - - // ... and files PageCache pageCache = pageCacheRule.getPageCache( fs ); File storeDir = directory.graphDbDir(); File neoStore = new File( storeDir, DEFAULT_NAME ); neoStore.createNewFile(); - - // ... and mocks Config config = mock( Config.class ); - AssertableLogProvider logProvider = new AssertableLogProvider(); - LogService logService = new SimpleLogService( NullLogProvider.getInstance(), logProvider ); + LogService logService = new SimpleLogService( NullLogProvider.getInstance(), NullLogProvider.getInstance() ); LegacyLogs legacyLogs = mock( LegacyLogs.class ); // when @@ -165,17 +159,45 @@ public void shouldGenerateTransactionInformationAsLastOption() throws Exception assertEquals( FIELD_NOT_PRESENT, getRecord( pageCache, neoStore, LAST_TRANSACTION_CHECKSUM ) ); assertEquals( FIELD_NOT_PRESENT, getRecord( pageCache, neoStore, LAST_TRANSACTION_COMMIT_TIMESTAMP ) ); // ... and transaction not in log - when( legacyLogs.getTransactionInformation( storeDir, txId ) ).thenThrow( NoSuchTransactionException.class ); + when( legacyLogs.getTransactionInformation( storeDir, txId ) ).thenReturn( Optional.empty() ); // ... and with migrator StoreMigrator migrator = new StoreMigrator( fs, pageCache, config, logService, schemaIndexProvider ); TransactionId actual = migrator.extractTransactionIdInformation( neoStore, storeDir, txId ); // then - logProvider.assertContainsMessageContaining( "Extraction of transaction " + txId + " from legacy logs failed."); - assertEquals( expected.transactionId(), actual.transactionId() ); + assertEquals( txId, actual.transactionId() ); assertEquals( TransactionIdStore.UNKNOWN_TX_CHECKSUM, actual.checksum() ); - assertEquals( expected.commitTimestamp(), actual.commitTimestamp() ); - // We do not expect checksum to be equal as it is randomly generated + assertEquals( TransactionIdStore.UNKNOWN_TX_COMMIT_TIMESTAMP, actual.commitTimestamp() ); + } + + @Test + public void shouldGenerateTransactionInformationWhenLogsAreEmpty() throws Exception + { + // given + long txId = 1; + PageCache pageCache = pageCacheRule.getPageCache( fs ); + File storeDir = directory.graphDbDir(); + File neoStore = new File( storeDir, DEFAULT_NAME ); + neoStore.createNewFile(); + Config config = mock( Config.class ); + LogService logService = new SimpleLogService( NullLogProvider.getInstance(), NullLogProvider.getInstance() ); + LegacyLogs legacyLogs = mock( LegacyLogs.class ); + + // when + // ... transaction info not in neo store + assertEquals( FIELD_NOT_PRESENT, getRecord( pageCache, neoStore, LAST_TRANSACTION_ID ) ); + assertEquals( FIELD_NOT_PRESENT, getRecord( pageCache, neoStore, LAST_TRANSACTION_CHECKSUM ) ); + assertEquals( FIELD_NOT_PRESENT, getRecord( pageCache, neoStore, LAST_TRANSACTION_COMMIT_TIMESTAMP ) ); + // ... and transaction not in log + when( legacyLogs.getTransactionInformation( storeDir, txId ) ).thenReturn( Optional.empty() ); + // ... and with migrator + StoreMigrator migrator = new StoreMigrator( fs, pageCache, config, logService, schemaIndexProvider ); + TransactionId actual = migrator.extractTransactionIdInformation( neoStore, storeDir, txId ); + + // then + assertEquals( txId, actual.transactionId() ); + assertEquals( TransactionIdStore.BASE_TX_CHECKSUM, actual.checksum() ); + assertEquals( TransactionIdStore.BASE_TX_COMMIT_TIMESTAMP, actual.commitTimestamp() ); } @Test diff --git a/community/neo4j/src/test/java/upgrade/StoreUpgraderTest.java b/community/neo4j/src/test/java/upgrade/StoreUpgraderTest.java index 29faa38a1121e..2744f16239bd0 100644 --- a/community/neo4j/src/test/java/upgrade/StoreUpgraderTest.java +++ b/community/neo4j/src/test/java/upgrade/StoreUpgraderTest.java @@ -23,11 +23,14 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.RuleChain; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.mockito.Mockito; import java.io.File; +import java.io.FilenameFilter; import java.io.IOException; import java.util.Arrays; import java.util.Collection; @@ -36,6 +39,7 @@ import org.neo4j.consistency.checking.full.ConsistencyCheckIncompleteException; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.graphdb.mockfs.DelegatingFileSystemAbstraction; import org.neo4j.helpers.collection.MapUtil; import org.neo4j.io.fs.DefaultFileSystemAbstraction; import org.neo4j.io.fs.FileSystemAbstraction; @@ -56,6 +60,7 @@ import org.neo4j.kernel.impl.store.format.standard.StandardV2_2; import org.neo4j.kernel.impl.store.format.standard.StandardV2_3; import org.neo4j.kernel.impl.store.format.standard.StandardV3_0; +import org.neo4j.kernel.impl.storemigration.MigrationTestUtils; import org.neo4j.kernel.impl.storemigration.StoreMigrationParticipant; import org.neo4j.kernel.impl.storemigration.StoreUpgrader; import org.neo4j.kernel.impl.storemigration.StoreUpgrader.UnableToUpgradeException; @@ -75,9 +80,11 @@ import org.neo4j.test.TestGraphDatabaseFactory; import static java.util.concurrent.TimeUnit.MINUTES; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyCollectionOf; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; @@ -86,6 +93,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; @@ -104,10 +112,15 @@ @RunWith(Parameterized.class) public class StoreUpgraderTest { + private final TestDirectory directory = TargetDirectory.testDirForTest( getClass() ); + private final PageCacheRule pageCacheRule = new PageCacheRule(); + private final ExpectedException expectedException = ExpectedException.none(); + @Rule - public final TestDirectory directory = TargetDirectory.testDirForTest( getClass() ); - @Rule - public final PageCacheRule pageCacheRule = new PageCacheRule(); + public final RuleChain ruleChain = RuleChain.outerRule( expectedException ) + .around( pageCacheRule ) + .around( directory ); + private File dbDirectory; private final FileSystemAbstraction fileSystem = new DefaultFileSystemAbstraction(); private StoreVersionCheck check; @@ -143,6 +156,53 @@ public void prepareDb() throws IOException prepareSampleLegacyDatabase( version, fileSystem, dbDirectory, prepareDirectory ); } + @Test + public void failMigrationWhenFailDuringTransactionInformationRetrieval() throws IOException + { + File storeDirectory = directory.graphDbDir(); + File prepare = directory.directory( "prepare" ); + FileSystemAbstraction fs = new DelegatingFileSystemAbstraction( fileSystem ) + { + @Override + public File[] listFiles( File directory, FilenameFilter filter ) + { + sneakyThrow( new IOException( "Enforced IO Exception Fail to open file" ) ); + return super.listFiles( directory, filter ); + } + + @Override + public boolean fileExists( File fileName ) + { + return true; + } + }; + + MigrationTestUtils.prepareSampleLegacyDatabase( version, fs, storeDirectory, prepare ); + // and a state of the migration saying that it has done the actual migration + PageCache pageCache = pageCacheRule.getPageCache( fs ); + UpgradableDatabase upgradableDatabase = new UpgradableDatabase( fs, new StoreVersionCheck( pageCache ), + new LegacyStoreVersionCheck( fs ), getRecordFormats() ) { + @Override + public RecordFormats checkUpgradeable( File storeDirectory ) + { + return getRecordFormats(); + } + }; + SilentMigrationProgressMonitor progressMonitor = new SilentMigrationProgressMonitor(); + + StoreMigrator defaultMigrator = new StoreMigrator( fs, pageCache, getTuningConfig(), NullLogService.getInstance(), + schemaIndexProvider ); + StoreUpgrader upgrader = new StoreUpgrader( upgradableDatabase, progressMonitor, allowMigrateConfig, fs, + NullLogProvider.getInstance() ); + upgrader.addParticipant( defaultMigrator ); + + expectedException.expect( UnableToUpgradeException.class ); + expectedException.expectCause( instanceOf( IOException.class ) ); + expectedException.expectCause( hasMessage( containsString( "Enforced IO Exception Fail to open file" ) ) ); + + upgrader.migrateIfNeeded( dbDirectory ); + } + @Test public void shouldUpgradeAnOldFormatStore() throws IOException, ConsistencyCheckIncompleteException { @@ -439,6 +499,11 @@ private StoreUpgrader newUpgrader( UpgradableDatabase upgradableDatabase, PageCa return upgrader; } + private static void sneakyThrow( Throwable throwable ) throws T + { + throw (T) throwable; + } + private List migrationHelperDirs() { File[] tmpDirs = dbDirectory.listFiles( ( file, name ) -> file.isDirectory() && diff --git a/enterprise/ha/src/main/java/org/neo4j/kernel/ha/TransactionChecksumLookup.java b/enterprise/ha/src/main/java/org/neo4j/kernel/ha/TransactionChecksumLookup.java index ef8f53b6b4915..6e80462d898de 100644 --- a/enterprise/ha/src/main/java/org/neo4j/kernel/ha/TransactionChecksumLookup.java +++ b/enterprise/ha/src/main/java/org/neo4j/kernel/ha/TransactionChecksumLookup.java @@ -40,12 +40,14 @@ public class TransactionChecksumLookup implements ThrowingLongUnaryOperator. + */ +package org.neo4j.kernel.ha; + +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import org.neo4j.kernel.impl.store.TransactionId; +import org.neo4j.kernel.impl.transaction.log.LogPosition; +import org.neo4j.kernel.impl.transaction.log.LogicalTransactionStore; +import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; +import org.neo4j.kernel.impl.transaction.log.TransactionMetadataCache; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TransactionChecksumLookupTest +{ + + private TransactionIdStore transactionIdStore = mock( TransactionIdStore.class ); + private LogicalTransactionStore transactionStore = mock( LogicalTransactionStore.class ); + + @Before + public void setUp() throws IOException + { + when( transactionIdStore.getLastCommittedTransaction() ).thenReturn( new TransactionId( 1, 1, 1 ) ); + when( transactionIdStore.getUpgradeTransaction() ).thenReturn( new TransactionId( 2, 2, 2 ) ); + when( transactionStore.getMetadataFor( 3 ) ).thenReturn( + new TransactionMetadataCache.TransactionMetadata( 1, 1, mock( LogPosition.class ), 3, 3 ) ); + } + + @Test + public void lookupChecksumUsingUpgradeTransaction() throws Exception + { + TransactionChecksumLookup checksumLookup = new TransactionChecksumLookup( transactionIdStore, transactionStore ); + assertEquals(2, checksumLookup.applyAsLong( 2 )); + } + + @Test + public void lookupChecksumUsingCommittedTransaction() throws Exception + { + TransactionChecksumLookup checksumLookup = new TransactionChecksumLookup( transactionIdStore, transactionStore ); + assertEquals(1, checksumLookup.applyAsLong( 1 )); + } + + @Test + public void lookupChecksumUsingTransactionStore() throws Exception + { + TransactionChecksumLookup checksumLookup = new TransactionChecksumLookup( transactionIdStore, transactionStore ); + assertEquals(3, checksumLookup.applyAsLong( 3 )); + } +}