public EagerlyReversedTransactionCursor( TransactionCursor cursor ) throws IOException { this.cursor = cursor; while ( cursor.next() ) { txs.add( cursor.get() ); } this.indexToReturn = txs.size(); }
private void readNextChunk() throws IOException { assert chunkStartOffsetIndex > 0; // Start at lowOffsetIndex - 1 and count backwards until almost reaching the chunk size long highOffset = chunkStartOffsetIndex == offsetsLength ? totalSize : offsets[chunkStartOffsetIndex]; int newLowOffsetIndex = chunkStartOffsetIndex; while ( newLowOffsetIndex > 0 ) { long deltaOffset = highOffset - offsets[--newLowOffsetIndex]; if ( deltaOffset > CHUNK_SIZE ) { // We've now read more than the read-ahead size, let's call this the end of this chunk break; } } assert chunkStartOffsetIndex - newLowOffsetIndex > 0; // We've established the chunk boundaries. Initialize all offsets and read the transactions in this // chunk into actual transaction objects int chunkLength = chunkStartOffsetIndex - newLowOffsetIndex; chunkStartOffsetIndex = newLowOffsetIndex; channel.setCurrentPosition( offsets[chunkStartOffsetIndex] ); assert chunkTransactions.isEmpty(); for ( int i = 0; i < chunkLength; i++ ) { boolean success = transactionCursor.next(); assert success; chunkTransactions.push( transactionCursor.get() ); } }
public static CommittedTransactionRepresentation[] exhaust( TransactionCursor cursor ) throws IOException { List<CommittedTransactionRepresentation> list = new ArrayList<>(); while ( cursor.next() ) { list.add( cursor.get() ); } return list.toArray( new CommittedTransactionRepresentation[list.size()] ); } }
@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; }
@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(); }
private static CommittedTransactionRepresentation extractLastTransaction( GraphDatabaseAPI db ) throws IOException { LogicalTransactionStore txStore = db.getDependencyResolver().resolveDependency( LogicalTransactionStore.class ); CommittedTransactionRepresentation transaction = null; try ( TransactionCursor cursor = txStore.getTransactions( TransactionIdStore.BASE_TX_ID + 1 ) ) { while ( cursor.next() ) { transaction = cursor.get(); } } return transaction; }
private static List<TransactionRepresentation> createConstraintCreatingTransactions() throws Exception { GraphDatabaseAPI db = (GraphDatabaseAPI) new TestGraphDatabaseFactory().newImpermanentDatabase(); try { try ( Transaction tx = db.beginTx() ) { db.schema().constraintFor( LABEL ).assertPropertyIsUnique( KEY ).create(); tx.success(); } LogicalTransactionStore txStore = db.getDependencyResolver().resolveDependency( LogicalTransactionStore.class ); List<TransactionRepresentation> result = new ArrayList<>(); try ( TransactionCursor cursor = txStore.getTransactions( TransactionIdStore.BASE_TX_ID + 1 ) ) { while ( cursor.next() ) { result.add( cursor.get().getTransactionRepresentation() ); } } return result; } finally { db.shutdown(); } } }
private void verifyTransaction( TransactionIdStore transactionIdStore, TransactionMetadataCache positionCache, byte[] additionalHeader, int masterId, int authorId, long timeStarted, long latestCommittedTxWhenStarted, long timeCommitted, LogicalTransactionStore store ) throws IOException { TransactionMetadata expectedMetadata; try ( TransactionCursor cursor = store.getTransactions( TransactionIdStore.BASE_TX_ID + 1 ) ) { boolean hasNext = cursor.next(); assertTrue( hasNext ); CommittedTransactionRepresentation tx = cursor.get(); TransactionRepresentation transaction = tx.getTransactionRepresentation(); assertArrayEquals( additionalHeader, transaction.additionalHeader() ); assertEquals( masterId, transaction.getMasterId() ); assertEquals( authorId, transaction.getAuthorId() ); assertEquals( timeStarted, transaction.getTimeStarted() ); assertEquals( timeCommitted, transaction.getTimeCommitted() ); assertEquals( latestCommittedTxWhenStarted, transaction.getLatestCommittedTxWhenStarted() ); expectedMetadata = new TransactionMetadata( masterId, authorId, tx.getStartEntry().getStartPosition(), tx.getStartEntry().checksum(), timeCommitted ); } positionCache.clear(); TransactionMetadata actualMetadata = store.getMetadataFor( transactionIdStore.getLastCommittedTransactionId() ); assertEquals( expectedMetadata, actualMetadata ); }
public EagerlyReversedTransactionCursor( TransactionCursor cursor ) throws IOException { this.cursor = cursor; while ( cursor.next() ) { txs.add( cursor.get() ); } this.indexToReturn = txs.size(); }
private void readNextChunk() throws IOException { assert chunkStartOffsetIndex > 0; // Start at lowOffsetIndex - 1 and count backwards until almost reaching the chunk size long highOffset = chunkStartOffsetIndex == offsetsLength ? totalSize : offsets[chunkStartOffsetIndex]; int newLowOffsetIndex = chunkStartOffsetIndex; while ( newLowOffsetIndex > 0 ) { long deltaOffset = highOffset - offsets[--newLowOffsetIndex]; if ( deltaOffset > CHUNK_SIZE ) { // We've now read more than the read-ahead size, let's call this the end of this chunk break; } } assert chunkStartOffsetIndex - newLowOffsetIndex > 0; // We've established the chunk boundaries. Initialize all offsets and read the transactions in this // chunk into actual transaction objects int chunkLength = chunkStartOffsetIndex - newLowOffsetIndex; chunkStartOffsetIndex = newLowOffsetIndex; channel.setCurrentPosition( offsets[chunkStartOffsetIndex] ); assert chunkTransactions.isEmpty(); for ( int i = 0; i < chunkLength; i++ ) { boolean success = transactionCursor.next(); assert success; chunkTransactions.push( transactionCursor.get() ); } }
@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 Optional<Long> getLatestTransactionLogIndex( long startTxId, File storeDir ) throws IOException { if ( !hasTxLogs( storeDir ) ) { return Optional.empty(); } // this is not really a read-only store, because it will create an empty transaction log if there is none ReadOnlyTransactionStore txStore = new ReadOnlyTransactionStore( pageCache, fs, storeDir, config, new Monitors() ); long lastTxId = BASE_TX_ID; try ( Lifespan ignored = new Lifespan( txStore ); TransactionCursor cursor = txStore.getTransactions( startTxId ) ) { while ( cursor.next() ) { CommittedTransactionRepresentation tx = cursor.get(); lastTxId = tx.getCommitEntry().getTxId(); } return Optional.of( lastTxId ); } catch ( NoSuchTransactionException e ) { return Optional.empty(); } }