/// <summary> /// Rotates the current log file, continuing into next (version) log file. /// This method must be recovery safe, which means a crash at any point should be recoverable. /// Concurrent readers must also be able to parry for concurrent rotation. /// Concurrent writes will not be an issue since rotation and writing contends on the same monitor. /// /// Steps during rotation are: /// <ol> /// <li>1: Increment log version, <seealso cref="LogVersionRepository.incrementAndGetVersion()"/> (also flushes the store)</li> /// <li>2: Flush current log</li> /// <li>3: Create new log file</li> /// <li>4: Write header</li> /// </ol> /// /// Recovery: what happens if crash between: /// <ol> /// <li>1-2: New log version has been set, starting the writer will create the new log file idempotently. /// At this point there may have been half-written transactions in the previous log version, /// although they haven't been considered committed and so they will be truncated from log during recovery</li> /// <li>2-3: New log version has been set, starting the writer will create the new log file idempotently. /// At this point there may be complete transactions in the previous log version which may not have been /// acknowledged to be committed back to the user, but will be considered committed anyway.</li> /// <li>3-4: New log version has been set, starting the writer will see that the new file exists and /// will be forgiving when trying to read the header of it, so that if it isn't complete a fresh /// header will be set.</li> /// </ol> /// /// Reading: what happens when rotation is between: /// <ol> /// <li>1-2: Reader bridge will see that there's a new version (when asking <seealso cref="LogVersionRepository"/> /// and try to open it. The log file doesn't exist yet though. The bridge can parry for this by catching /// <seealso cref="FileNotFoundException"/> and tell the reader that the stream has ended</li> /// <li>2-3: Same as (1-2)</li> /// <li>3-4: Here the new log file exists, but the header may not be fully written yet. /// the reader will fail when trying to read the header since it's reading it strictly and bridge /// catches that exception, treating it the same as if the file didn't exist.</li> /// </ol> /// </summary> /// <param name="currentLog"> current <seealso cref="LogVersionedStoreChannel channel"/> to flush and close. </param> /// <returns> the channel of the newly opened/created log file. </returns> /// <exception cref="IOException"> if an error regarding closing or opening log files occur. </exception> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#: //ORIGINAL LINE: private org.neo4j.kernel.impl.transaction.log.PhysicalLogVersionedStoreChannel rotate(org.neo4j.kernel.impl.transaction.log.LogVersionedStoreChannel currentLog) throws java.io.IOException private PhysicalLogVersionedStoreChannel Rotate(LogVersionedStoreChannel currentLog) { /* * The store is now flushed. If we fail now the recovery code will open the * current log file and replay everything. That's unnecessary but totally ok. */ long newLogVersion = _logVersionRepository.incrementAndGetVersion(); /* * Rotation can happen at any point, although not concurrently with an append, * although an append may have (most likely actually) left at least some bytes left * in the buffer for future flushing. Flushing that buffer now makes the last appended * transaction complete in the log we're rotating away. Awesome. */ _writer.prepareForFlush().flush(); /* * The log version is now in the store, flushed and persistent. If we crash * now, on recovery we'll attempt to open the version we're about to create * (but haven't yet), discover it's not there. That will lead to creating * the file, setting the header and continuing. * We using committing transaction id as a source of last transaction id here since * we can have transactions that are not yet published as committed but were already stored * into transaction log that was just rotated. */ PhysicalLogVersionedStoreChannel newLog = _logFiles.createLogChannelForVersion(newLogVersion, OpenMode.READ_WRITE, _context.committingTransactionId); currentLog.close(); return(newLog); }
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#: //ORIGINAL LINE: public LogVersionedStoreChannel next(LogVersionedStoreChannel channel) throws java.io.IOException public override LogVersionedStoreChannel Next(LogVersionedStoreChannel channel) { PhysicalLogVersionedStoreChannel nextChannel; try { nextChannel = _logFiles.openForVersion(channel.Version + 1); } catch (Exception e) when(e is FileNotFoundException || e is IncompleteLogHeaderException) { // See PhysicalLogFile#rotate() for description as to why these exceptions are OK return(channel); } channel.close(); return(nextChannel); }
/// <summary> /// Extracts txId from first commit entry, when starting reading at the given {@code position}. /// If no commit entry found in the version, the reader will continue into next version(s) up till /// {@code maxLogVersion} until finding one. /// </summary> /// <param name="initialPosition"> <seealso cref="LogPosition"/> to start scan from. </param> /// <param name="maxLogVersion"> max log version to scan. </param> /// <returns> value object that contains first transaction id of closes commit entry to {@code initialPosition}, /// or <seealso cref="LogTailInformation.NO_TRANSACTION_ID"/> if not found. And failure flag that will be set to true if /// there was some exception during transaction log processing. </returns> /// <exception cref="IOException"> on channel close I/O error. </exception> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#: //ORIGINAL LINE: protected ExtractedTransactionRecord extractFirstTxIdAfterPosition(org.neo4j.kernel.impl.transaction.log.LogPosition initialPosition, long maxLogVersion) throws java.io.IOException protected internal virtual ExtractedTransactionRecord ExtractFirstTxIdAfterPosition(LogPosition initialPosition, long maxLogVersion) { LogPosition currentPosition = initialPosition; while (currentPosition.LogVersion <= maxLogVersion) { LogVersionedStoreChannel storeChannel = TryOpenStoreChannel(currentPosition); if (storeChannel != null) { try { storeChannel.Position(currentPosition.ByteOffset); using (ReadAheadLogChannel logChannel = new ReadAheadLogChannel(storeChannel), LogEntryCursor cursor = new LogEntryCursor(_logEntryReader, logChannel)) { while (cursor.Next()) { LogEntry entry = cursor.Get(); if (entry is LogEntryCommit) { return(new ExtractedTransactionRecord((( LogEntryCommit )entry).TxId)); } } } } catch (Exception t) { _monitor.corruptedLogFile(currentPosition.LogVersion, t); return(new ExtractedTransactionRecord(true)); } finally { storeChannel.close(); } } currentPosition = LogPosition.start(currentPosition.LogVersion + 1); } return(new ExtractedTransactionRecord()); }