@Override public RecoveryStartInformation getRecoveryStartInformation() { return new RecoveryStartInformation( LogPosition.start( 0 ), 1 ); }
long getTimestampForVersion( long version ) throws IOException { LogPosition position = LogPosition.start( version ); try ( ReadableLogChannel channel = logFiles.getLogFile().getReader( position ) ) { LogEntry entry; while ( (entry = logEntryReader.readLogEntry( channel )) != null ) { if ( entry instanceof LogEntryStart ) { return entry.<LogEntryStart>as().getTimeWritten(); } } } return -1; } }
@Override public boolean next() throws IOException { while ( currentLogTransactionCursor == null || !currentLogTransactionCursor.next() ) { // We've run out of transactions in this log version, back up to a previous one currentVersion--; if ( currentVersion < backToPosition.getLogVersion() ) { return false; } closeCurrent(); LogPosition position = currentVersion > backToPosition.getLogVersion() ? start( currentVersion ) : backToPosition; currentLogTransactionCursor = cursorFactory.apply( position ); } return true; }
private ThrowingFunction<LogPosition,TransactionCursor,IOException> log( int... transactionCounts ) throws IOException { long baseOffset = LogPosition.start( 0 ).getByteOffset(); @SuppressWarnings( "unchecked" ) ThrowingFunction<LogPosition,TransactionCursor,IOException> result = mock( ThrowingFunction.class ); AtomicLong txId = new AtomicLong( 0 ); CommittedTransactionRepresentation[][] logs = new CommittedTransactionRepresentation[transactionCounts.length][]; for ( int logVersion = 0; logVersion < transactionCounts.length; logVersion++ ) { logs[logVersion] = transactions( transactionCounts[logVersion], txId ); } when( result.apply( any( LogPosition.class ) ) ).thenAnswer( invocation -> { LogPosition position = invocation.getArgument( 0 ); if ( position == null ) { // A mockito issue when calling the "when" methods, I believe return null; } // For simplicity the offset means, in this test, the array offset CommittedTransactionRepresentation[] transactions = logs[toIntExact( position.getLogVersion() )]; CommittedTransactionRepresentation[] subset = copyOfRange( transactions, toIntExact( position.getByteOffset() - baseOffset ), transactions.length ); ArrayUtil.reverse( subset ); return given( subset ); } ); return result; }
@Test public void reportProgressOnRecovery() throws Throwable { RecoveryService recoveryService = mock( RecoveryService.class, Answers.RETURNS_MOCKS ); CorruptedLogsTruncator logsTruncator = mock( CorruptedLogsTruncator.class ); RecoveryMonitor recoveryMonitor = mock( RecoveryMonitor.class ); TransactionCursor reverseTransactionCursor = mock( TransactionCursor.class ); TransactionCursor transactionCursor = mock( TransactionCursor.class ); CommittedTransactionRepresentation transactionRepresentation = mock( CommittedTransactionRepresentation.class ); int transactionsToRecover = 5; int expectedMax = transactionsToRecover * 2; int lastCommittedTransactionId = 14; LogPosition recoveryStartPosition = LogPosition.start( 0 ); int firstTxIdAfterLastCheckPoint = 10; RecoveryStartInformation startInformation = new RecoveryStartInformation( recoveryStartPosition, firstTxIdAfterLastCheckPoint ); when( reverseTransactionCursor.next() ).thenAnswer( new NextTransactionAnswer( transactionsToRecover ) ); when( transactionCursor.next() ).thenAnswer( new NextTransactionAnswer( transactionsToRecover ) ); when( reverseTransactionCursor.get() ).thenReturn( transactionRepresentation ); when( transactionCursor.get() ).thenReturn( transactionRepresentation ); when( transactionRepresentation.getCommitEntry() ).thenReturn( new LogEntryCommit( lastCommittedTransactionId, 1L ) ); when( recoveryService.getRecoveryStartInformation() ).thenReturn( startInformation ); when( recoveryService.getTransactionsInReverseOrder( recoveryStartPosition ) ).thenReturn( reverseTransactionCursor ); when( recoveryService.getTransactions( recoveryStartPosition ) ).thenReturn( transactionCursor ); AssertableProgressReporter progressReporter = new AssertableProgressReporter( expectedMax ); Recovery recovery = new Recovery( recoveryService, logsTruncator, recoveryMonitor, progressReporter, true ); recovery.init(); progressReporter.verify(); }
@Test public void doNotPruneEmptyLogs() throws IOException { logPruner.truncate( LogPosition.start( 0 ) ); assertTrue( FileUtils.isEmptyDirectory( databaseDirectory ) ); }
@Test public void shouldHandleEmptyLogsMidStream() throws Exception { // GIVEN TransactionCursor cursor = new ReversedMultiFileTransactionCursor( log( 5, 0, 2, 0, 3 ), 4, start( 0 ) ); // WHEN CommittedTransactionRepresentation[] reversed = exhaust( cursor ); // THEN assertTransactionRange( reversed, 5 + 2 + 3, 0 ); }
@Test public void shouldReadMultipleVersionsReversed() throws Exception { // GIVEN TransactionCursor cursor = new ReversedMultiFileTransactionCursor( log( 5, 3, 8 ), 2, start( 0 ) ); // WHEN CommittedTransactionRepresentation[] reversed = exhaust( cursor ); // THEN assertTransactionRange( reversed, 5 + 3 + 8, 0 ); }
@Test public void shouldReadSingleVersionReversed() throws Exception { // GIVEN TransactionCursor cursor = new ReversedMultiFileTransactionCursor( log( 5 ), 0, start( 0 ) ); // WHEN CommittedTransactionRepresentation[] reversed = exhaust( cursor ); // THEN assertTransactionRange( reversed, 5, 0 ); }
@Test public void shouldHandleEmptySingleLogVersion() throws Exception { // GIVEN TransactionCursor cursor = new ReversedMultiFileTransactionCursor( log( 0 ), 0, start( 0 ) ); // WHEN CommittedTransactionRepresentation[] reversed = exhaust( cursor ); // THEN assertTransactionRange( reversed, 0, 0 ); }
private ReversedSingleFileTransactionCursor txCursor( boolean failOnCorruptedLogFiles ) throws IOException { ReadAheadLogChannel fileReader = (ReadAheadLogChannel) logFile.getReader( start( 0 ), NO_MORE_CHANNELS ); try { return new ReversedSingleFileTransactionCursor( fileReader, new VersionAwareLogEntryReader<>(), failOnCorruptedLogFiles, monitor ); } catch ( UnsupportedLogVersionException e ) { fileReader.close(); throw e; } }
@Test public void shouldRecoverFromStartOfLogZeroIfThereAreNoCheckPointAndOldestLogIsVersionZero() { // given when( tailScanner.getTailInformation() ).thenReturn( new LogTailInformation( true, 10L, INITIAL_LOG_VERSION, currentLogVersion, LogEntryVersion.CURRENT ) ); // when RecoveryStartInformation recoveryStartInformation = new RecoveryStartInformationProvider( tailScanner, monitor ).get(); // then verify( monitor ).noCheckPointFound(); assertEquals( LogPosition.start( INITIAL_LOG_VERSION ), recoveryStartInformation.getRecoveryPosition() ); assertEquals( 10L, recoveryStartInformation.getFirstTxIdAfterLastCheckPoint() ); assertTrue( recoveryStartInformation.isRecoveryRequired() ); }
LogPosition position = LogPosition.start( logVersion ); if ( !visitor.visit( position, lowTransactionId, highTransactionId ) )
return createRecoveryInformation( LogPosition.start( 0 ), txIdAfterLastCheckPoint );
@Test public void shouldDetectAndPreventChannelReadingMultipleLogVersions() throws Exception { // given writeTransactions( 1, 1, 1 ); logFile.rotate(); writeTransactions( 1, 1, 1 ); // when try ( ReadAheadLogChannel channel = (ReadAheadLogChannel) logFile.getReader( start( 0 ) ) ) { new ReversedSingleFileTransactionCursor( channel, new VersionAwareLogEntryReader<>(), false, monitor ); fail( "Should've failed" ); } catch ( IllegalArgumentException e ) { // then good assertThat( e.getMessage(), containsString( "multiple log versions" ) ); } }
); ReadableLogChannel logChannel = logFiles.getLogFile().getReader( LogPosition.start( version ) );
currentPosition = LogPosition.start( currentPosition.getLogVersion() + 1 );
private void removeLastCheckpointRecordFromLastLogFile() throws IOException { LogPosition checkpointPosition = null; LogFile transactionLogFile = logFiles.getLogFile(); VersionAwareLogEntryReader<ReadableLogChannel> entryReader = new VersionAwareLogEntryReader<>(); LogPosition startPosition = LogPosition.start( logFiles.getHighestLogVersion() ); try ( ReadableLogChannel reader = transactionLogFile.getReader( startPosition ) ) { LogEntry logEntry; do { logEntry = entryReader.readLogEntry( reader ); if ( logEntry instanceof CheckPoint ) { checkpointPosition = ((CheckPoint) logEntry).getLogPosition(); } } while ( logEntry != null ); } if ( checkpointPosition != null ) { try ( StoreChannel storeChannel = fileSystemRule.open( logFiles.getHighestLogFile(), OpenMode.READ_WRITE ) ) { storeChannel.truncate( checkpointPosition.getByteOffset() ); } } }
long getTimestampForVersion( long version ) throws IOException { LogPosition position = LogPosition.start( version ); try ( ReadableLogChannel channel = logFiles.getLogFile().getReader( position ) ) { LogEntry entry; while ( (entry = logEntryReader.readLogEntry( channel )) != null ) { if ( entry instanceof LogEntryStart ) { return entry.<LogEntryStart>as().getTimeWritten(); } } } return -1; } }
@Override public boolean next() throws IOException { while ( currentLogTransactionCursor == null || !currentLogTransactionCursor.next() ) { // We've run out of transactions in this log version, back up to a previous one currentVersion--; if ( currentVersion < backToPosition.getLogVersion() ) { return false; } closeCurrent(); LogPosition position = currentVersion > backToPosition.getLogVersion() ? start( currentVersion ) : backToPosition; currentLogTransactionCursor = cursorFactory.apply( position ); } return true; }