/// <summary> /// Creates or finds the log stream. /// If being created either initializes the log with default log records or records from backup log. /// </summary> /// <param name="openMode">Open mode of the replica.</param> /// <returns>Task that represents the asynchronous open operation.</returns> internal async Task <PhysicalLogReader> OpenAsync(ReplicaOpenMode openMode) { // TODO: Anurag: do we plumb c.token up? this.LogicalLog = await this.CreateLogFileAsync(openMode == ReplicaOpenMode.New, CancellationToken.None).ConfigureAwait(false); var logLogLength = this.LogicalLog.Length; if (logLogLength <= sizeof(int)) { // No usable content in the log if (this.LogicalLog.WritePosition > 0) { await this.LogicalLog.TruncateTail(0, CancellationToken.None).ConfigureAwait(false); // Remove all contents and reset write cursor back to 0 Utility.Assert(this.LogicalLog.Length == 0, "this.logicalLog.Length == 0"); Utility.Assert(this.LogicalLog.WritePosition == 0, "this.logicalLog.WritePosition == 0"); } using ( var logWriter = new PhysicalLogWriter( this.LogicalLog, this.emptyCallbackManager, this.Tracer, this.MaxWriteCacheSizeInMB, this.IncomingBytesRateCounterWriter, this.LogFlushBytesRateCounterWriter, this.BytesPerFlushCounterWriter, this.AvgFlushLatencyCounterWriter, this.AvgSerializationLatencyCounterWriter, false)) { var zeroIndexRecord = IndexingLogRecord.CreateZeroIndexingLogRecord(); logWriter.InsertBufferedRecord(zeroIndexRecord); logWriter.InsertBufferedRecord(UpdateEpochLogRecord.CreateZeroUpdateEpochLogRecord()); var zeroBeginCheckpointRecord = BeginCheckpointLogRecord.CreateZeroBeginCheckpointLogRecord(); logWriter.InsertBufferedRecord(zeroBeginCheckpointRecord); logWriter.InsertBufferedRecord(BarrierLogRecord.CreateOneBarrierLogRecord()); var oneEndCheckpointRecord = EndCheckpointLogRecord.CreateOneEndCheckpointLogRecord( zeroBeginCheckpointRecord, zeroIndexRecord); logWriter.InsertBufferedRecord(oneEndCheckpointRecord); var endCompleteCheckpointRecord = new CompleteCheckpointLogRecord( LogicalSequenceNumber.OneLsn, zeroIndexRecord, oneEndCheckpointRecord); logWriter.InsertBufferedRecord(endCompleteCheckpointRecord); await logWriter.FlushAsync("OpenAsync").ConfigureAwait(false); // This additional await is required to ensure the log record was indeed flushed. // Without this, the flushasync could succeed, but the log record flush could have failed due to a write error await endCompleteCheckpointRecord.AwaitFlush().ConfigureAwait(false); } } return(new PhysicalLogReader(this)); }
/// <summary> /// Process the complete checkpoint log record. /// </summary> /// <param name="completeCheckpointLogRecord">The complete checkpoint record to be processed.</param> /// <param name="isRecoverableRecord">Is this a recoverable record.</param> private void ProcessLogRecord( CompleteCheckpointLogRecord completeCheckpointLogRecord, out bool isRecoverableRecord) { if (this.mode == Mode.Restore) { completeCheckpointLogRecord.HeadRecord = this.firstIndexingLogRecord; } isRecoverableRecord = false; this.LastLinkedPhysicalRecord = completeCheckpointLogRecord; }
/// /// Append complete checkpoint record /// public void CompleteCheckpoint() { lock (lsnOrderingLock) { var record = new CompleteCheckpointLogRecord( this.CurrentLogTailLsn, this.CurrentLogHeadRecord, this.LastLinkedPhysicalRecord); this.LastLinkedPhysicalRecord = record; this.LogManager.PhysicalLogWriter.InsertBufferedRecord(record); } }
public async Task <RecoveryInformation> OpenAsync( ReplicaOpenMode openMode, TransactionalReplicatorSettings replicatorSettings, ITracer tracer, LogManager logManager, bool shouldLocalStateBeRemoved, PhysicalLogReader recoveryReader, bool isRestoring) { this.RecoveryLogsReader = recoveryReader; this.tracer = tracer; PhysicalLogRecord recoveredLinkedPhysicalRecord; TruncateHeadLogRecord recoveredLastTruncateHeadLogRecord = null; CompleteCheckpointLogRecord recoveredCompleteCheckpointRecord = null; this.logCompleteCheckpointAfterRecovery = true; this.tailRecordAtStart = await this.RecoveryLogsReader.SeekToLastRecord().ConfigureAwait(false); var logUsage = logManager.Length; var trace = "OpenAsync: Log Usage: " + logUsage + " Recovered Replica Tail Record Type: " + tailRecordAtStart.RecordType + " LSN: " + tailRecordAtStart.Lsn.LSN + " PSN: " + tailRecordAtStart.Psn.PSN + " Position: " + tailRecordAtStart.RecordPosition; FabricEvents.Events.Api(tracer.Type, trace); var lastPhysicalRecord = tailRecordAtStart as PhysicalLogRecord; if (lastPhysicalRecord == null) { lastPhysicalRecord = await this.RecoveryLogsReader.GetPreviousPhysicalRecord(tailRecordAtStart).ConfigureAwait(false); } trace = "OpenAsync: Recovered last physical record Type: " + lastPhysicalRecord.RecordType + " LSN: " + lastPhysicalRecord.Lsn.LSN + " PSN: " + lastPhysicalRecord.Psn.PSN + " Position: " + lastPhysicalRecord.RecordPosition; FabricEvents.Events.Api(tracer.Type, trace); if (lastPhysicalRecord.RecordType == LogRecordType.Information) { var removingStateRecord = lastPhysicalRecord as InformationLogRecord; if (removingStateRecord.InformationEvent == InformationEvent.RemovingState) { Utility.Assert( lastPhysicalRecord == tailRecordAtStart, "Last Physical Record {0} should be same as tail record at start {1}", lastPhysicalRecord, tailRecordAtStart); FabricEvents.Events.Api(tracer.Type, "OpenAsync: Skipping Recovery due to pending RemovingState"); this.IsRemovingStateAfterOpen = true; return(new RecoveryInformation(shouldLocalStateBeRemoved)); } } if (lastPhysicalRecord.RecordType == LogRecordType.TruncateHead) { recoveredLastTruncateHeadLogRecord = lastPhysicalRecord as TruncateHeadLogRecord; } else if (lastPhysicalRecord.RecordType == LogRecordType.EndCheckpoint) { this.RecoveredLastEndCheckpointRecord = lastPhysicalRecord as EndCheckpointLogRecord; } else if (lastPhysicalRecord.RecordType == LogRecordType.CompleteCheckpoint) { recoveredCompleteCheckpointRecord = lastPhysicalRecord as CompleteCheckpointLogRecord; } else { recoveredLinkedPhysicalRecord = await this.RecoveryLogsReader.GetLinkedPhysicalRecord(lastPhysicalRecord).ConfigureAwait(false); Utility.Assert( (recoveredLinkedPhysicalRecord != null) && ((recoveredLinkedPhysicalRecord.RecordType == LogRecordType.TruncateHead) || (recoveredLinkedPhysicalRecord.RecordType == LogRecordType.EndCheckpoint) || (recoveredLinkedPhysicalRecord.RecordType == LogRecordType.CompleteCheckpoint)), "Record type should be truncate head or end checkpoint or complete checkpoint. Record type is : {0}", recoveredLinkedPhysicalRecord.RecordType); trace = "OpenAsync: RecoveredLinkedPhysicalRecord: " + recoveredLinkedPhysicalRecord.RecordType + " LSN: " + recoveredLinkedPhysicalRecord.Lsn.LSN + " PSN: " + recoveredLinkedPhysicalRecord.Psn.PSN + " Position: " + recoveredLinkedPhysicalRecord.RecordPosition; FabricEvents.Events.Api(tracer.Type, trace); if (recoveredLinkedPhysicalRecord.RecordType == LogRecordType.TruncateHead) { recoveredLastTruncateHeadLogRecord = recoveredLinkedPhysicalRecord as TruncateHeadLogRecord; } else if (recoveredLinkedPhysicalRecord.RecordType == LogRecordType.EndCheckpoint) { this.RecoveredLastEndCheckpointRecord = recoveredLinkedPhysicalRecord as EndCheckpointLogRecord; } else { recoveredCompleteCheckpointRecord = recoveredLinkedPhysicalRecord as CompleteCheckpointLogRecord; } } ulong logHeadPosition = 0; long logHeadLsn = 0; if (recoveredLastTruncateHeadLogRecord != null) { logHeadPosition = recoveredLastTruncateHeadLogRecord.LogHeadRecordPosition; logHeadLsn = recoveredLastTruncateHeadLogRecord.LogHeadLsn.LSN; trace = "OpenAsync: Recovered last truncate head record Type: " + recoveredLastTruncateHeadLogRecord.RecordType + " LSN: " + recoveredLastTruncateHeadLogRecord.Lsn.LSN + " PSN: " + recoveredLastTruncateHeadLogRecord.Psn.PSN + " Position: " + recoveredLastTruncateHeadLogRecord.RecordPosition + " LogHeadLSN: " + recoveredLastTruncateHeadLogRecord.LogHeadLsn.LSN + " LogHeadPosition: " + recoveredLastTruncateHeadLogRecord.LogHeadRecordPosition; FabricEvents.Events.Api(tracer.Type, trace); recoveredLinkedPhysicalRecord = recoveredLastTruncateHeadLogRecord; do { recoveredLinkedPhysicalRecord = await this.RecoveryLogsReader.GetLinkedPhysicalRecord(recoveredLinkedPhysicalRecord).ConfigureAwait(false); Utility.Assert( (recoveredLinkedPhysicalRecord != null) && ((recoveredLinkedPhysicalRecord.RecordType == LogRecordType.TruncateHead) || (recoveredLinkedPhysicalRecord.RecordType == LogRecordType.EndCheckpoint) || (recoveredLinkedPhysicalRecord.RecordType == LogRecordType.CompleteCheckpoint)), "Record type should be truncate head or end checkpoint or complete checkpoint. Record type is : {0}", recoveredLinkedPhysicalRecord.RecordType); this.RecoveredLastEndCheckpointRecord = recoveredLinkedPhysicalRecord as EndCheckpointLogRecord; if (recoveredLinkedPhysicalRecord.RecordType == LogRecordType.CompleteCheckpoint) { recoveredCompleteCheckpointRecord = recoveredLinkedPhysicalRecord as CompleteCheckpointLogRecord; } } while (this.RecoveredLastEndCheckpointRecord == null); } else if (recoveredCompleteCheckpointRecord != null) { logHeadPosition = recoveredCompleteCheckpointRecord.LogHeadRecordPosition; logHeadLsn = recoveredCompleteCheckpointRecord.LogHeadLsn.LSN; trace = "OpenAsync: Recovered last complete checkpoint record Type: " + recoveredCompleteCheckpointRecord.RecordType + " LSN: " + recoveredCompleteCheckpointRecord.Lsn.LSN + " PSN: " + recoveredCompleteCheckpointRecord.Psn.PSN + " Position: " + recoveredCompleteCheckpointRecord.RecordPosition + " LogHeadLsn: " + recoveredCompleteCheckpointRecord.LogHeadLsn.LSN + " LogHeadPosition: " + recoveredCompleteCheckpointRecord.LogHeadRecordPosition; FabricEvents.Events.Api(tracer.Type, trace); recoveredLinkedPhysicalRecord = recoveredCompleteCheckpointRecord; do { recoveredLinkedPhysicalRecord = await this.RecoveryLogsReader.GetLinkedPhysicalRecord(recoveredLinkedPhysicalRecord).ConfigureAwait(false); Utility.Assert( (recoveredLinkedPhysicalRecord != null) && ((recoveredLinkedPhysicalRecord.RecordType == LogRecordType.TruncateHead) || (recoveredLinkedPhysicalRecord.RecordType == LogRecordType.EndCheckpoint) || (recoveredLinkedPhysicalRecord.RecordType == LogRecordType.CompleteCheckpoint)), "Record type should be truncate head or end checkpoint or complete checkpoint. Record type is : {0}", recoveredLinkedPhysicalRecord.RecordType); this.RecoveredLastEndCheckpointRecord = recoveredLinkedPhysicalRecord as EndCheckpointLogRecord; } while (this.RecoveredLastEndCheckpointRecord == null); } else { logHeadPosition = this.RecoveredLastEndCheckpointRecord.LogHeadRecordPosition; logHeadLsn = this.RecoveredLastEndCheckpointRecord.LogHeadLsn.LSN; } trace = "OpenAsync: Recovered last end checkpoint record Type: " + this.RecoveredLastEndCheckpointRecord.RecordType + " LSN: " + this.RecoveredLastEndCheckpointRecord.Lsn.LSN + " PSN: " + this.RecoveredLastEndCheckpointRecord.Psn.PSN + " Position: " + this.RecoveredLastEndCheckpointRecord.RecordPosition + " LogHeadLSN: " + this.RecoveredLastEndCheckpointRecord.LogHeadLsn.LSN + " LogHeadPosition: " + this.RecoveredLastEndCheckpointRecord.LogHeadRecordPosition; FabricEvents.Events.Api(tracer.Type, trace); if (recoveredCompleteCheckpointRecord != null) { this.logCompleteCheckpointAfterRecovery = false; // This is fine since both the begin and end records guarantee all the SP's have been renamed trace = "OpenAsync: " + " LogHeadPosition: " + this.RecoveredLastEndCheckpointRecord.LogHeadRecordPosition + Environment.NewLine + " LogHeadLSN: " + this.RecoveredLastEndCheckpointRecord.LogHeadLsn.LSN + Environment.NewLine + " CompleteCheckpoint record found with LSN: " + recoveredCompleteCheckpointRecord.Lsn.LSN + " PSN: " + recoveredCompleteCheckpointRecord.Psn.PSN + " Position: " + recoveredCompleteCheckpointRecord.RecordPosition; FabricEvents.Events.Api(tracer.Type, trace); } else { this.logCompleteCheckpointAfterRecovery = true; trace = "OpenAsync: " + " LogHeadPosition: " + this.RecoveredLastEndCheckpointRecord.LogHeadRecordPosition + Environment.NewLine + " LogHeadLSN: " + this.RecoveredLastEndCheckpointRecord.LogHeadLsn.LSN + Environment.NewLine + " CompleteCheckpoint record missing"; FabricEvents.Events.Api(tracer.Type, trace); } // For restore cases, logHeadPosition must be 0. if (isRestoring == true) { Utility.Assert( logHeadPosition == 0, "Full backup: LogHead Position ({0}) must be 0", logHeadPosition); } this.RecoveryLogsReader.MoveStartingRecordPosition( logHeadLsn, logHeadPosition, "recovery", LogManager.LogReaderType.Recovery); logManager.SetLogHeadRecordPosition(logHeadPosition); return(new RecoveryInformation( this.logCompleteCheckpointAfterRecovery, shouldLocalStateBeRemoved)); }
public async Task <LogRecord> TruncateTailAsync() { Utility.Assert( tailLsn < this.replicatedLogManager.CurrentLogTailLsn, "tailLsn < this.currentLogTailLsn. Current log tail lsn: {0}", this.replicatedLogManager.CurrentLogTailLsn.LSN); Utility.Assert( tailLsn >= this.replicatedLogManager.LastCompletedBeginCheckpointRecord.Lsn, "tailLsn >= this.LastCompletedBeginCheckpointRecord.LastLogicalSequenceNumber. LastCompletedBeginCheckpointLogRecord: {0}", this.replicatedLogManager.LastCompletedBeginCheckpointRecord.Lsn); Utility.Assert( this.replicatedLogManager.LastInProgressTruncateHeadRecord == null, "this.lastInProgressTruncateHeadRecord == null"); var currentRecord = this.replicatedLogManager.LogManager.CurrentLogTailRecord; var currentLsn = currentRecord.Lsn; var isUpdateRecordAtTail = true; var recoveredLsn = currentLsn; EndCheckpointLogRecord endCheckpointLogRecord = null; CompleteCheckpointLogRecord completeCheckpointLogRecord = null; var lastPhysicalRecord = this.replicatedLogManager.LogManager.CurrentLastPhysicalRecord; do { Utility.Assert( LogRecord.IsInvalidRecord(currentRecord) == false, "LogRecord.IsInvalidRecord(currentRecord ({0})) == false", currentRecord); if (isUpdateRecordAtTail == true) { isUpdateRecordAtTail = currentRecord.Lsn == recoveredLsn; } OperationData metaData = null; switch (currentRecord.RecordType) { case LogRecordType.BeginTransaction: var beginTransactionRecord = (BeginTransactionOperationLogRecord)currentRecord; // Cache the latest metadata just read from disk metaData = beginTransactionRecord.MetaData; beginTransactionRecord = this.transactionsMap.DeleteTransaction(beginTransactionRecord); // Reset the metadata of the transaction as it may have been modified during redo pass beginTransactionRecord.MetaData = metaData; if (beginTransactionRecord.IsSingleOperationTransaction) { Utility.Assert( beginTransactionRecord.Lsn != LogicalSequenceNumber.InvalidLsn, "begin transaction record lsn must not be invalid."); beginTransactionRecord.Transaction.CommitSequenceNumber = beginTransactionRecord.Lsn.LSN; var operationContext = await this.stateManager.OnApplyAsync( beginTransactionRecord.Lsn.LSN, beginTransactionRecord.Transaction, beginTransactionRecord.MetaData, beginTransactionRecord.Undo, falseProgressApplyContext).ConfigureAwait(false); if (operationContext != null) { this.stateManager.Unlock(operationContext); } FabricEvents.Events.TruncateTailSingleOperationTransactionRecord( this.tracer.Type, "Deleted", beginTransactionRecord.Lsn.LSN, beginTransactionRecord.Psn.PSN, beginTransactionRecord.RecordPosition, beginTransactionRecord.Transaction.Id); } else { FabricEvents.Events.TruncateTailTransactionRecord( this.tracer.Type, "Deleted", beginTransactionRecord.Lsn.LSN, beginTransactionRecord.Psn.PSN, beginTransactionRecord.RecordPosition, beginTransactionRecord.Transaction.Id); } break; case LogRecordType.Operation: var operationRecord = (OperationLogRecord)currentRecord; // Cache the latest metadata just read from disk metaData = operationRecord.MetaData; operationRecord = this.transactionsMap.RedactOperation(operationRecord); // Reset the metadata of the operation as it may have been modified during redo pass operationRecord.MetaData = metaData; if (operationRecord.Transaction.IsAtomicOperation == true) { Utility.Assert( operationRecord.IsRedoOnly == false, "TruncateTail- RedoOnly operation cannot be undone"); Utility.Assert( operationRecord.Lsn != LogicalSequenceNumber.InvalidLsn, "Operation's lsn must not be invalid."); operationRecord.Transaction.CommitSequenceNumber = operationRecord.Lsn.LSN; var operationContext = await this.stateManager.OnApplyAsync( operationRecord.Lsn.LSN, operationRecord.Transaction, operationRecord.MetaData, operationRecord.Undo, falseProgressApplyContext).ConfigureAwait(false); if (operationContext != null) { this.stateManager.Unlock(operationContext); } FabricEvents.Events.TruncateTailAtomicOperation( this.tracer.Type, operationRecord.Lsn.LSN, operationRecord.Psn.PSN, operationRecord.RecordPosition, operationRecord.Transaction.Id); } else { FabricEvents.Events.TruncateTailOperationRecord( this.tracer.Type, "Deleted", operationRecord.Lsn.LSN, operationRecord.Psn.PSN, operationRecord.RecordPosition, operationRecord.Transaction.Id); } break; case LogRecordType.EndTransaction: var endTransactionRecord = (EndTransactionLogRecord)currentRecord; endTransactionRecord = this.transactionsMap.ReifyTransaction(endTransactionRecord); Utility.Assert( endTransactionRecord.Lsn != LogicalSequenceNumber.InvalidLsn, "end transaction record cannot have an invalid lsn."); // Undo all operations (Call apply with undo). if (endTransactionRecord.IsCommitted == true) { TransactionLogRecord transactionRecord = endTransactionRecord; do { // During recovery operations that may be undo are kept in memory. // Since Truncate tail uses the in-memory links, Transaction have already been created and their commit sequence numbers // have been set during recovery redo. Utility.Assert( transactionRecord.RecordType == LogRecordType.EndTransaction || transactionRecord.Transaction.CommitSequenceNumber != LogicalSequenceNumber.InvalidLsn.LSN, "For an operation to be undone, it must already have been done."); object operationContext = null; transactionRecord = transactionRecord.ParentTransactionRecord; Utility.Assert(transactionRecord != null, "transactionRecord != null"); if (transactionRecord is BeginTransactionOperationLogRecord) { // Cache the metadata read from disk var justReadTransactionRecord = await this.recoveryLogsReader.GetNextLogRecord(transactionRecord.RecordPosition).ConfigureAwait(false); Utility.Assert( justReadTransactionRecord.RecordType == LogRecordType.BeginTransaction, "Just read transaction during false progress is not begintransaction. It is {0}", justReadTransactionRecord.RecordType); var justReadBeginTransactionRecord = (BeginTransactionOperationLogRecord)justReadTransactionRecord; var beginTx = (BeginTransactionOperationLogRecord)transactionRecord; beginTx.MetaData = justReadBeginTransactionRecord.MetaData; Utility.Assert( beginTx.IsSingleOperationTransaction == false, "beginTx.IsSingleOperationTransaction must be false when endTxRecord is being processed"); operationContext = await this.stateManager.OnApplyAsync( beginTx.Lsn.LSN, beginTx.Transaction, beginTx.MetaData, beginTx.Undo, falseProgressApplyContext).ConfigureAwait(false); if (operationContext != null) { beginTx.OperationContext = operationContext; } break; } // Cache the metadata read from disk var justReadOperationLogRecord = await this.recoveryLogsReader.GetNextLogRecord(transactionRecord.RecordPosition).ConfigureAwait(false); Utility.Assert( justReadOperationLogRecord.RecordType == LogRecordType.Operation, "Just read operation during false progress is not of the right type. It is {0}", justReadOperationLogRecord.RecordType); var justReadOperationRecord = (OperationLogRecord)justReadOperationLogRecord; operationRecord = (OperationLogRecord)transactionRecord; operationRecord.MetaData = justReadOperationRecord.MetaData; operationContext = await this.stateManager.OnApplyAsync( operationRecord.Lsn.LSN, operationRecord.Transaction, operationRecord.MetaData, operationRecord.Undo, falseProgressApplyContext).ConfigureAwait(false); if (operationContext != null) { operationRecord.OperationContext = operationContext; } } while (true); // call unlock transactionRecord = endTransactionRecord; do { object operationContext = null; transactionRecord = transactionRecord.ParentTransactionRecord; Utility.Assert(transactionRecord != null, "transactionRecord != null"); if (transactionRecord is BeginTransactionOperationLogRecord) { var beginTx = (BeginTransactionOperationLogRecord)transactionRecord; operationContext = beginTx.ResetOperationContext(); if (operationContext != null) { this.stateManager.Unlock(operationContext); } FabricEvents.Events.TruncateTailOperationRecord( this.tracer.Type, "Undone", beginTx.Lsn.LSN, beginTx.Psn.PSN, beginTx.RecordPosition, beginTx.Transaction.Id); break; } operationRecord = (OperationLogRecord)transactionRecord; operationContext = operationRecord.ResetOperationContext(); if (operationContext != null) { this.stateManager.Unlock(operationContext); } FabricEvents.Events.TruncateTailOperationRecord( this.tracer.Type, "Undone", operationRecord.Lsn.LSN, operationRecord.Psn.PSN, operationRecord.RecordPosition, operationRecord.Transaction.Id); } while (true); } FabricEvents.Events.TruncateTailTransactionRecord( this.tracer.Type, "Reified", endTransactionRecord.Lsn.LSN, endTransactionRecord.Psn.PSN, endTransactionRecord.RecordPosition, endTransactionRecord.Transaction.Id); break; case LogRecordType.Barrier: var barrierRecord = (BarrierLogRecord)currentRecord; FabricEvents.Events.TruncateTailBarrier( this.tracer.Type, barrierRecord.Lsn.LSN, barrierRecord.Psn.PSN, barrierRecord.RecordPosition); break; case LogRecordType.Backup: // Inform the backup manager that the last backup log record has been undone. this.backupManager.UndoLastCompletedBackupLogRecord(); // Trace that the backup log record has been false progressed. var backupRecord = (BackupLogRecord)currentRecord; #if !DotNetCoreClr // These are new events defined in System.Fabric, existing CoreCLR apps would break // if these events are refernced as it wont be found. As CoreCLR apps carry System.Fabric // along with application // This is just a mitigation for now. Actual fix being tracked via bug# 11614507 FabricEvents.Events.TruncateTailBackup( this.tracer.Type, backupRecord.Lsn.LSN, backupRecord.Psn.PSN, backupRecord.RecordPosition); #endif break; case LogRecordType.UpdateEpoch: // These records can only occur at the tail Utility.Assert(isUpdateRecordAtTail == true, "isUpdateRecordAtTail == true"); var updateEpochRecord = (UpdateEpochLogRecord)currentRecord; var lastVector = new ProgressVectorEntry(updateEpochRecord); this.replicatedLogManager.ProgressVector.TruncateTail(lastVector); FabricEvents.Events.TruncateTailUpdateEpoch( this.tracer.Type, lastVector.Epoch.DataLossNumber, lastVector.Epoch.ConfigurationNumber, updateEpochRecord.Lsn.LSN, updateEpochRecord.Psn.PSN, updateEpochRecord.RecordPosition); break; case LogRecordType.EndCheckpoint: Utility.Assert( currentRecord.Psn == this.replicatedLogManager.LastCompletedEndCheckpointRecord.Psn, "currentRecord.Psn == this.lastCompletedEndCheckpointRecord.Psn"); Utility.Assert( currentRecord.Psn == this.replicatedLogManager.LastLinkedPhysicalRecord.Psn, "currentRecord.Psn == this.lastLinkedPhysicalRecord.Psn"); endCheckpointLogRecord = this.replicatedLogManager.LastCompletedEndCheckpointRecord; this.replicatedLogManager.OnTruncateTailOfLastLinkedPhysicalRecord(); goto case LogRecordType.Indexing; case LogRecordType.TruncateHead: Utility.Assert( currentRecord.Psn == this.replicatedLogManager.LastLinkedPhysicalRecord.Psn, "currentRecord.Psn == this.lastLinkedPhysicalRecord.Psn"); var truncateHeadRecord = (TruncateHeadLogRecord)currentRecord; Utility.Assert( truncateHeadRecord.IsStable == false, "Stable truncateHeadRecord cannot be undone due to false progress"); this.replicatedLogManager.OnTruncateTailOfLastLinkedPhysicalRecord(); goto case LogRecordType.Indexing; case LogRecordType.CompleteCheckpoint: completeCheckpointLogRecord = currentRecord as CompleteCheckpointLogRecord; this.replicatedLogManager.OnTruncateTailOfLastLinkedPhysicalRecord(); goto case LogRecordType.Indexing; case LogRecordType.Indexing: case LogRecordType.TruncateTail: case LogRecordType.BeginCheckpoint: case LogRecordType.Information: Utility.Assert( currentRecord.Psn == lastPhysicalRecord.Psn, "currentRecord.Psn == lastPhysicalRecord.Psn"); lastPhysicalRecord = lastPhysicalRecord.PreviousPhysicalRecord; break; default: Utility.CodingError("Unexpected record type {0}", currentRecord.RecordType); break; } currentRecord = await this.recoveryLogsReader.GetPreviousLogRecord(currentRecord.RecordPosition).ConfigureAwait(false); currentLsn = currentRecord.Lsn; } while (currentLsn > tailLsn); Utility.Assert(currentLsn == tailLsn, "V1 replicator ensures that lsns are continuous. currentLsn {0} == tailLsn {1}", currentLsn, tailLsn); if (currentRecord is LogicalLogRecord) { switch (currentRecord.RecordType) { case LogRecordType.BeginTransaction: var beginTransactionRecord = (BeginTransactionOperationLogRecord)currentRecord; currentRecord = this.transactionsMap.FindTransaction(beginTransactionRecord); // Single operation transactions are not stored in the tx map and hence are not returned above. As a result, they dont have a valid indexing of the previous physical record if (beginTransactionRecord.IsSingleOperationTransaction) { currentRecord.PreviousPhysicalRecord = lastPhysicalRecord; } break; case LogRecordType.Operation: var operationRecord = (OperationLogRecord)currentRecord; currentRecord = this.transactionsMap.FindOperation(operationRecord); // Atomic operations are not stored in the tx map and hence are not returned above. As a result, they dont have a valid indexing of the previous physical record if (operationRecord.Transaction.IsAtomicOperation) { currentRecord.PreviousPhysicalRecord = lastPhysicalRecord; } break; case LogRecordType.EndTransaction: var endTransactionRecord = (EndTransactionLogRecord)currentRecord; currentRecord = this.transactionsMap.FindUnstableTransaction(endTransactionRecord); break; case LogRecordType.Backup: case LogRecordType.Barrier: currentRecord.PreviousPhysicalRecord = lastPhysicalRecord; break; case LogRecordType.UpdateEpoch: currentRecord.PreviousPhysicalRecord = lastPhysicalRecord; break; default: Utility.CodingError("Unexpected record type {0}", currentRecord.RecordType); break; } Utility.Assert( currentRecord.PreviousPhysicalRecord == lastPhysicalRecord, "currentRecord.PreviousPhysicalRecord == lastPhysicalRecord"); } else { Utility.Assert( lastPhysicalRecord.Psn == currentRecord.Psn, "lastPhysicalRecord.Psn == currentRecord.Psn"); currentRecord = lastPhysicalRecord; } Utility.Assert( (this.replicatedLogManager.LastLinkedPhysicalRecord == lastPhysicalRecord) || (this.replicatedLogManager.LastLinkedPhysicalRecord == lastPhysicalRecord.LinkedPhysicalRecord), "(this.lastLinkedPhysicalRecord == lastPhysicalRecord) || (this.lastLinkedPhysicalRecord == lastPhysicalRecord.LinkedPhysicalRecord)"); await this.replicatedLogManager.LogManager.PerformLogTailTruncationAsync(currentRecord).ConfigureAwait(false); this.replicatedLogManager.SetTailLsn(tailLsn); // If endchkpoint was truncated, even complete checkpoint must be truncated if (endCheckpointLogRecord != null) { Utility.Assert( completeCheckpointLogRecord != null, "TruncateTailAsync: EndCheckpoint was truncated but CompleteCheckpoint was not"); this.replicatedLogManager.EndCheckpoint(endCheckpointLogRecord.LastCompletedBeginCheckpointRecord); } if (completeCheckpointLogRecord != null) { this.replicatedLogManager.CompleteCheckpoint(); } this.replicatedLogManager.TruncateTail(tailLsn); await this.replicatedLogManager.LogManager.FlushAsync("TruncateTailAsync").ConfigureAwait(false); FabricEvents.Events.TruncateTailDone( this.tracer.Type, currentRecord.RecordType.ToString(), currentRecord.Lsn.LSN, currentRecord.Psn.PSN, currentRecord.RecordPosition); return(currentRecord); }
private static LogRecord ReadRecord(BinaryReader br, ulong recordPosition, bool isPhysicalRead) { LogRecord record; var lsn = LogicalSequenceNumber.InvalidLsn.LSN; LogRecordType recordType; // Metadata section. var startingPosition = br.BaseStream.Position; var sizeOfSection = br.ReadInt32(); var endPosition = startingPosition + sizeOfSection; // Read Logical Metadata recordType = (LogRecordType)br.ReadUInt32(); switch (recordType) { case LogRecordType.BeginTransaction: record = new BeginTransactionOperationLogRecord(recordType, recordPosition, lsn); break; case LogRecordType.Operation: record = new OperationLogRecord(recordType, recordPosition, lsn); break; case LogRecordType.EndTransaction: record = new EndTransactionLogRecord(recordType, recordPosition, lsn); break; case LogRecordType.Barrier: record = new BarrierLogRecord(recordType, recordPosition, lsn); break; case LogRecordType.UpdateEpoch: record = new UpdateEpochLogRecord(recordType, recordPosition, lsn); break; case LogRecordType.Backup: record = new BackupLogRecord(recordType, recordPosition, lsn); break; case LogRecordType.BeginCheckpoint: record = new BeginCheckpointLogRecord(recordType, recordPosition, lsn); break; case LogRecordType.EndCheckpoint: record = new EndCheckpointLogRecord(recordType, recordPosition, lsn); break; case LogRecordType.Indexing: record = new IndexingLogRecord(recordType, recordPosition, lsn); break; case LogRecordType.TruncateHead: record = new TruncateHeadLogRecord(recordType, recordPosition, lsn); break; case LogRecordType.TruncateTail: record = new TruncateTailLogRecord(recordType, recordPosition, lsn); break; case LogRecordType.Information: record = new InformationLogRecord(recordType, recordPosition, lsn); break; case LogRecordType.CompleteCheckpoint: record = new CompleteCheckpointLogRecord(recordType, recordPosition, lsn); break; default: Utility.CodingError("Unexpected record type {0}", recordType); return(null); } record.lsn = new LogicalSequenceNumber(br.ReadInt64()); // Jump to the end of the section ignoring fields that are not understood. Utility.Assert(endPosition >= br.BaseStream.Position, "Could not have read more than section size."); br.BaseStream.Position = endPosition; record.Read(br, isPhysicalRead); return(record); }