internal EndTransactionLogRecord FindUnstableTransaction(EndTransactionLogRecord record) { Utility.Assert( record.Transaction.IsAtomicOperation == false, "record.Transaction.IsAtomicOperation == false"); Utility.Assert( LogRecord.IsInvalidRecord(record.ParentTransactionRecord) == true, "LogRecord.IsInvalidRecord(record.ParentTransactionRecord) == true"); int i; lock (this.txmapLock) { for (i = this.unstableTransactions.Count - 1; i >= 0; i--) { if (this.unstableTransactions[i].Transaction == record.Transaction) { record = this.unstableTransactions[i]; break; } } } Utility.Assert( i >= 0, "Transaction associated with the end transaction log record is not presnet in unstable transactions"); return(record); }
internal OperationLogRecord RedactOperation(OperationLogRecord record) { Utility.Assert(record.Transaction.IsValidTransaction, "record.Transaction.IsValidTransaction == true"); Utility.Assert(record.IsOperationContextPresent == false, "record.IsOperationContextPresent == false"); if (record.Transaction.IsAtomicOperation) { Utility.Assert(record.ParentTransactionRecord == null, "record.ParentTransactionRecord == null"); } else { Utility.Assert( LogRecord.IsInvalidRecord(record.ParentTransactionRecord), "LogRecord.IsInvalidRecord(record.ParentTransactionRecord) == true"); lock (this.txmapLock) { Utility.Assert( this.latestRecords.ContainsKey(record.Transaction.Id), "latestRecords_.ContainsKey(record.Transaction.Id)"); record = (OperationLogRecord)this.latestRecords[record.Transaction.Id]; this.latestRecords[record.Transaction.Id] = record.ParentTransactionRecord; record.ParentTransactionRecord.ChildTransactionRecord = TransactionLogRecord.InvalidTransactionLogRecord; return(record); } } return(record); }
internal OperationLogRecord FindOperation(OperationLogRecord record) { Utility.Assert(record.Transaction.IsValidTransaction, "record.Transaction.IsValidTransaction == true"); Utility.Assert(record.IsOperationContextPresent == false, "record.IsOperationContextPresent == false"); if (record.Transaction.IsAtomicOperation) { Utility.Assert(record.ParentTransactionRecord == null, "record.ParentTransactionRecord == null"); } else { Utility.Assert( LogRecord.IsInvalidRecord(record.ParentTransactionRecord), "LogRecord.IsInvalidRecord(record.ParentTransactionRecord) == true"); lock (this.txmapLock) { Utility.Assert( this.latestRecords.ContainsKey(record.Transaction.Id), "Could not find operation in latest records"); return((OperationLogRecord)this.latestRecords[record.Transaction.Id]); } } return(record); }
/// <summary> /// Inserts a record to the buffer where it waits to be flushed in batches. /// A successful run will set following properties in the record. /// record.Psn and record.PreviousPhysicalLogRecord /// </summary> /// <param name="record"></param> /// <returns>The pending size of the buffered records</returns> internal long InsertBufferedRecord(LogRecord record) { Utility.Assert(LogRecord.IsInvalidRecord(record) == false, "LogRecord.IsInvalidRecord(record) == false"); var physicalRecord = record as PhysicalLogRecord; lock (this.flushLock) { if (this.closedException == null) { // No record can be inserted after 'RemovingState' Information log record if (this.lastPhysicalRecord != null && this.lastPhysicalRecord.RecordType == LogRecordType.Information) { var lastInformationRecord = this.lastPhysicalRecord as InformationLogRecord; Utility.Assert( lastInformationRecord.InformationEvent != InformationEvent.RemovingState, "No record should be inserted after 'RemovingState'. Current Record = {0}", record); } // Update record and tail ++this.currentLogTailPsn; record.Psn = this.currentLogTailPsn; this.incomingBytesRateCounterWriter.IncrementBy(record.ApproximateSizeOnDisk); record.PreviousPhysicalRecord = this.lastPhysicalRecord; if (physicalRecord != null) { if (this.lastPhysicalRecord != null) { this.lastPhysicalRecord.NextPhysicalRecord = physicalRecord; } this.lastPhysicalRecord = physicalRecord; } // Buffer record if (this.bufferedRecords == null) { this.bufferedRecords = new List <LogRecord>(); } this.bufferedRecords.Add(record); var newBufferedRecordsBytes = Interlocked.Add( ref this.bufferedRecordsBytes, record.ApproximateSizeOnDisk); return(newBufferedRecordsBytes); } } var failedRecord = new LoggedRecords(record, this.closedException); this.ProcessFlushedRecords(failedRecord); return(Interlocked.Read(ref this.bufferedRecordsBytes)); }
internal async Task IndexPhysicalRecords(PhysicalLogRecord record) { while (record.RecordPosition > this.startingRecordPosition) { record = await this.GetPreviousPhysicalRecord(record).ConfigureAwait(false); } Utility.Assert( LogRecord.IsInvalidRecord(this.startingRecord) == false, "LogRecord.IsInvalidRecord(this.startingRecord) == false"); }
internal async Task <LogRecord> SeekToLastRecord() { Utility.Assert(this.endingRecordPosition == long.MaxValue, "this.endingRecordPosition == Int64.MaxValue"); Utility.Assert( LogRecord.IsInvalidRecord(this.endRecord), "LogRecord.IsInvalidRecord(this.endRecord) == true. this.endRecord: {0}", this.endRecord); this.readStream.Position = this.readStream.Length; this.endRecord = await LogRecord.ReadPreviousRecordAsync(this.readStream).ConfigureAwait(false); return(this.endRecord); }
internal async Task <PhysicalLogRecord> GetPreviousPhysicalRecord(LogRecord record) { var previousPhysicalRecord = record.PreviousPhysicalRecord; if (LogRecord.IsInvalidRecord(previousPhysicalRecord)) { var previousPhysicalRecordOffset = record.PreviousPhysicalRecordOffset; Utility.Assert( previousPhysicalRecordOffset != LogRecord.InvalidPhysicalRecordOffset, "previousPhysicalRecordOffset != LogRecord.INVALID_PHYSICAL_RECORD_OFFSET"); if (previousPhysicalRecordOffset == 0) { record.PreviousPhysicalRecord = null; return(null); } // Read previous physical record var recordPosition = record.RecordPosition; Utility.Assert( recordPosition != LogRecord.InvalidRecordPosition, "recordPosition != LogRecord.INVALID_RECORD_POSITION"); var previousPhysicalRecordPosition = recordPosition - previousPhysicalRecordOffset; Utility.Assert( previousPhysicalRecordPosition >= this.startingRecordPosition, "previousPhysicalRecordPosition >= this.startingRecordPosition"); previousPhysicalRecord = (PhysicalLogRecord)await this.GetNextLogRecord(previousPhysicalRecordPosition).ConfigureAwait(false); var nextPhysicalLogRecord = record as PhysicalLogRecord; if (nextPhysicalLogRecord != null) { previousPhysicalRecord.NextPhysicalRecord = nextPhysicalLogRecord; } record.PreviousPhysicalRecord = previousPhysicalRecord; if (previousPhysicalRecordPosition == this.startingRecordPosition) { this.startingRecord = previousPhysicalRecord; } } Utility.Assert( (previousPhysicalRecord == null) == (record.PreviousPhysicalRecordOffset == 0), "(previousPhysicalRecord == null) == (record.PreviousPhysicalRecordOffset == 0)"); return(previousPhysicalRecord); }
internal async Task <BeginCheckpointLogRecord> GetLastCompletedBeginCheckpointRecord( EndCheckpointLogRecord record) { var lastCompletedBeginCheckpointRecord = record.LastCompletedBeginCheckpointRecord; if (!LogRecord.IsInvalidRecord(lastCompletedBeginCheckpointRecord)) { return(lastCompletedBeginCheckpointRecord); } var lastCompletedBeginCheckpointRecordOffset = record.LastCompletedBeginCheckpointRecordOffset; Utility.Assert( lastCompletedBeginCheckpointRecordOffset > 0, "lastCompletedBeginCheckpointRecordOffset {0} > 0", lastCompletedBeginCheckpointRecordOffset); // Read desired checkpoint record var recordPosition = record.RecordPosition; Utility.Assert( recordPosition != LogRecord.InvalidRecordPosition, "recordPosition ({0}) != LogRecord.INVALID_RECORD_POSITION", recordPosition); var lastCompletedBeginCheckpointRecordPosition = recordPosition - lastCompletedBeginCheckpointRecordOffset; Utility.Assert( lastCompletedBeginCheckpointRecordPosition >= this.startingRecordPosition, "lastCompletedBeginCheckpointRecordPosition ({0}) >= this.startingRecordPosition ({1})", lastCompletedBeginCheckpointRecordPosition, this.startingRecordPosition); lastCompletedBeginCheckpointRecord = (BeginCheckpointLogRecord)await this.GetNextLogRecord(lastCompletedBeginCheckpointRecordPosition).ConfigureAwait(false); record.LastCompletedBeginCheckpointRecord = lastCompletedBeginCheckpointRecord; return(lastCompletedBeginCheckpointRecord); }
internal async Task <PhysicalLogRecord> GetLinkedPhysicalRecord(PhysicalLogRecord record) { var linkedPhysicalRecord = record.LinkedPhysicalRecord; if (LogRecord.IsInvalidRecord(linkedPhysicalRecord)) { var linkedPhysicalRecordOffset = record.LinkedPhysicalRecordOffset; Utility.Assert( linkedPhysicalRecordOffset != LogRecord.InvalidPhysicalRecordOffset, "linkedPhysicalRecordOffset != PhysicalLogRecord.INVALID_PHYSICAL_RECORD_OFFSET"); if (linkedPhysicalRecordOffset == 0) { record.LinkedPhysicalRecord = null; return(null); } // Read desired linked record var recordPosition = record.RecordPosition; Utility.Assert( recordPosition != LogRecord.InvalidRecordPosition, "recordPosition != LogRecord.INVALID_RECORD_POSITION"); var linkedPhysicalRecordPosition = recordPosition - linkedPhysicalRecordOffset; Utility.Assert( linkedPhysicalRecordPosition >= this.startingRecordPosition, "linkedPhysicalRecordPosition >= this.startingRecordPosition"); linkedPhysicalRecord = (PhysicalLogRecord)await this.GetNextLogRecord(linkedPhysicalRecordPosition).ConfigureAwait(false); record.LinkedPhysicalRecord = linkedPhysicalRecord; } Utility.Assert( (linkedPhysicalRecord == null) == (record.LinkedPhysicalRecordOffset == 0), "(linkedPhysicalRecord == null) == (record.LinkedPhysicalRecordOffset == 0)"); return(linkedPhysicalRecord); }
internal EndTransactionLogRecord ReifyTransaction(EndTransactionLogRecord record) { Utility.Assert( record.Transaction.IsAtomicOperation == false, "record.Transaction.IsAtomicOperation == false"); Utility.Assert( LogRecord.IsInvalidRecord(record.ParentTransactionRecord) == true, "LogRecord.IsInvalidRecord(record.ParentTransactionRecord) == true"); BeginTransactionOperationLogRecord reifiedBeginTransactionRecord = null; EndTransactionLogRecord reifiedEndTransactionRecord = null; int i; lock (this.txmapLock) { for (i = this.completedTransactions.Count - 1; i >= 0; i--) { if (this.completedTransactions[i].Transaction == record.Transaction) { reifiedBeginTransactionRecord = this.completedTransactions[i]; Utility.Assert( reifiedBeginTransactionRecord.IsSingleOperationTransaction == false, "reifiedBeginTransactionRecord.IsSingleOperationTransaction == false"); this.completedTransactions.RemoveAt(i); break; } } Utility.Assert(i > -1, "Begin transaction record is not present in completed transactions"); for (i = this.unstableTransactions.Count - 1; i >= 0; i--) { if (this.unstableTransactions[i].Transaction == record.Transaction) { reifiedEndTransactionRecord = this.unstableTransactions[i]; this.unstableTransactions.RemoveAt(i); break; } } Utility.Assert(i > -1, "End transaction record is not present in unstable transactions"); this.latestRecords[record.Transaction.Id] = reifiedEndTransactionRecord.ParentTransactionRecord; reifiedEndTransactionRecord.ParentTransactionRecord.ChildTransactionRecord = TransactionLogRecord.InvalidTransactionLogRecord; TransactionLogRecord parentRecord = reifiedEndTransactionRecord; BeginTransactionOperationLogRecord chainedBeginTransactionRecord; do { parentRecord = parentRecord.ParentTransactionRecord; chainedBeginTransactionRecord = parentRecord as BeginTransactionOperationLogRecord; } while (chainedBeginTransactionRecord == null); Utility.Assert( reifiedBeginTransactionRecord == chainedBeginTransactionRecord, "reifiedBeginTransactionRecord == chainedBeginTransactionRecord"); Utility.Assert( reifiedBeginTransactionRecord.IsEnlistedTransaction, "reifiedBeginTransactionRecord.IsEnlistedTransaction == true"); this.transactionIdPendingTransactionsPair[reifiedBeginTransactionRecord.Transaction.Id] = reifiedBeginTransactionRecord; this.lsnPendingTransactionsPair[reifiedBeginTransactionRecord.Lsn.LSN] = reifiedBeginTransactionRecord; } return(reifiedEndTransactionRecord); }
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 async Task ApplyCallback(LogRecord record) { Utility.Assert( this.roleContextDrainState.DrainingStream != DrainingStream.Invalid && this.roleContextDrainState.DrainingStream != DrainingStream.StateStream, "ApplyCallback: this.roleContextDrainState.DrainingStream != DrainingStream.Invalid && this.roleContextDrainState.DrainingStream != DrainingStream.StateStream. It is {0} for log record lsn: {1} and psn: {2}", this.roleContextDrainState.DrainingStream, record.Lsn, record.Psn); var serviceException = this.serviceException; if (serviceException != null) { record.CompletedApply(serviceException); return; } try { OperationLogRecord operationRecord; var callbackRedoContext = this.roleContextDrainState.ApplyRedoContext; switch (record.RecordType) { case LogRecordType.BeginTransaction: var beginTransactionRecordExtra = (BeginTransactionOperationLogRecord)record; FabricEvents.Events.ApplyCallbackTransactionRecordNoise( this.tracer.Type, (int)this.roleContextDrainState.DrainingStream, (uint)beginTransactionRecordExtra.RecordType, beginTransactionRecordExtra.Lsn.LSN, beginTransactionRecordExtra.Psn.PSN, beginTransactionRecordExtra.RecordPosition, beginTransactionRecordExtra.Transaction.Id); if (beginTransactionRecordExtra.IsSingleOperationTransaction) { if (beginTransactionRecordExtra.Lsn > this.recoveredOrCopiedCheckpointLsn.Value) { beginTransactionRecordExtra.Transaction.CommitSequenceNumber = beginTransactionRecordExtra.Lsn.LSN; var operationContext = await this.stateManager.OnApplyAsync( beginTransactionRecordExtra.Lsn.LSN, beginTransactionRecordExtra.Transaction, beginTransactionRecordExtra.MetaData, beginTransactionRecordExtra.Redo, callbackRedoContext).ConfigureAwait(false); if (operationContext != null) { beginTransactionRecordExtra.OperationContext = operationContext; } FabricEvents.Events.ApplyCallbackSingleOperationTransaction( this.tracer.Type, (int)this.roleContextDrainState.DrainingStream, beginTransactionRecordExtra.Lsn.LSN, beginTransactionRecordExtra.Psn.PSN, beginTransactionRecordExtra.RecordPosition, beginTransactionRecordExtra.Transaction.Id); } } break; case LogRecordType.Operation: operationRecord = (OperationLogRecord)record; if (operationRecord.Transaction.IsAtomicOperation == true) { if (operationRecord.Lsn > this.recoveredOrCopiedCheckpointLsn.Value) { // For atomic operations create lsn equals commit lsn. operationRecord.Transaction.CommitSequenceNumber = operationRecord.Lsn.LSN; var operationContext = await this.stateManager.OnApplyAsync( operationRecord.Lsn.LSN, operationRecord.Transaction, operationRecord.MetaData, operationRecord.Redo, callbackRedoContext).ConfigureAwait(false); if (operationContext != null) { operationRecord.OperationContext = operationContext; } FabricEvents.Events.ApplyCallbackAtomicOperationRecord( this.tracer.Type, (int)this.roleContextDrainState.DrainingStream, operationRecord.Lsn.LSN, operationRecord.Psn.PSN, operationRecord.RecordPosition, operationRecord.Transaction.Id, operationRecord.IsRedoOnly); } } else { FabricEvents.Events.ApplyCallbackTransactionRecordNoise( this.tracer.Type, (int)this.roleContextDrainState.DrainingStream, (uint)operationRecord.RecordType, operationRecord.Lsn.LSN, operationRecord.Psn.PSN, operationRecord.RecordPosition, operationRecord.Transaction.Id); } break; case LogRecordType.EndTransaction: var endTransactionRecord = (EndTransactionLogRecord)record; if ((endTransactionRecord.IsCommitted == true) && (endTransactionRecord.Lsn > this.recoveredOrCopiedCheckpointLsn.Value)) { // GopalK: I am currently adopting the approach of only applying updates that do not // belong to any transaction in the update switch above and // applying the updates that belong to a transaction only when if it commits. // The other approach is to immediately apply updates of all // transactions in the update switch case above and then undoing updates of // aborted transactions here. // Both approaches have their pros and cons and we may want to look into // making this a replicator option that the service developer can choose. // If on Primary, Transaction object is shared. endTransactionRecord.Transaction.CommitSequenceNumber = endTransactionRecord.Lsn.LSN; BeginTransactionOperationLogRecord beginTransactionRecord = null; TransactionLogRecord transactionRecord = endTransactionRecord; do { transactionRecord = transactionRecord.ParentTransactionRecord; beginTransactionRecord = transactionRecord as BeginTransactionOperationLogRecord; } while (beginTransactionRecord == null); { Utility.Assert( LogRecord.IsInvalidRecord(beginTransactionRecord) == false, "TransactionLogRecord.IsInvalidRecord(beginTransactionRecord) == false"); Utility.Assert( beginTransactionRecord.IsSingleOperationTransaction == false, "beginTransactionRecord.IsSingleOperationTransaction must be false when endTxRecord is being processed"); // If on not Primary, Transaction object is shared. if (callbackRedoContext.HasFlag(ApplyContext.PRIMARY) == false) { beginTransactionRecord.Transaction.CommitSequenceNumber = endTransactionRecord.Lsn.LSN; } else { // TODO: Temporary assert. Should be removed later. Utility.Assert( beginTransactionRecord.Transaction.CommitSequenceNumber == endTransactionRecord.Lsn.LSN, "Transaction's commit sequence number must have already been set. Expected: {0} Actual: {1}", transactionRecord.Transaction.CommitSequenceNumber, endTransactionRecord.Lsn.LSN); } var operationContext = await this.stateManager.OnApplyAsync( beginTransactionRecord.Lsn.LSN, beginTransactionRecord.Transaction, beginTransactionRecord.MetaData, beginTransactionRecord.Redo, callbackRedoContext).ConfigureAwait(false); if (operationContext != null) { beginTransactionRecord.OperationContext = operationContext; } FabricEvents.Events.ApplyCallbackTransactionRecordNoise( this.tracer.Type, (int)this.roleContextDrainState.DrainingStream, (uint)beginTransactionRecord.RecordType, beginTransactionRecord.Lsn.LSN, beginTransactionRecord.Psn.PSN, beginTransactionRecord.RecordPosition, beginTransactionRecord.Transaction.Id); } do { transactionRecord = transactionRecord.ChildTransactionRecord; Utility.Assert( (transactionRecord != null) && (LogRecord.IsInvalidRecord(transactionRecord) == false), "(transactionRecord != null) && (TransactionLogRecord.IsInvalidRecord(transactionRecord) == false"); if (transactionRecord == endTransactionRecord) { break; } operationRecord = (OperationLogRecord)transactionRecord; // If on Primary, Transaction object is shared. if (callbackRedoContext.HasFlag(ApplyContext.PRIMARY) == false) { operationRecord.Transaction.CommitSequenceNumber = endTransactionRecord.Lsn.LSN; } else { // TODO: Temporary assert. Should be removed later. Utility.Assert( operationRecord.Transaction.CommitSequenceNumber == endTransactionRecord.Lsn.LSN, "Transaction's commit sequence number must have already been set. Expected: {0} Actual: {1}", transactionRecord.Transaction.CommitSequenceNumber, endTransactionRecord.Lsn.LSN); } var operationContext = await this.stateManager.OnApplyAsync( operationRecord.Lsn.LSN, operationRecord.Transaction, operationRecord.MetaData, operationRecord.Redo, callbackRedoContext).ConfigureAwait(false); if (operationContext != null) { operationRecord.OperationContext = operationContext; } FabricEvents.Events.ApplyCallbackTransactionRecordNoise( this.tracer.Type, (int)this.roleContextDrainState.DrainingStream, (uint)operationRecord.RecordType, operationRecord.Lsn.LSN, operationRecord.Psn.PSN, operationRecord.RecordPosition, operationRecord.Transaction.Id); } while (true); FabricEvents.Events.ApplyCallbackTransactionRecord( this.tracer.Type, (int)this.roleContextDrainState.DrainingStream, endTransactionRecord.Psn.PSN, endTransactionRecord.Transaction.Id); } break; case LogRecordType.Barrier: this.LastAppliedBarrierRecord = (BarrierLogRecord)record; FabricEvents.Events.ApplyCallbackBarrierRecord( this.tracer.Type, (int)this.roleContextDrainState.DrainingStream, record.Lsn.LSN, record.Psn.PSN, record.RecordPosition); break; case LogRecordType.Backup: // TODO: Trace. break; default: Utility.CodingError("Unexpected record type {0}", record.RecordType); break; } } catch (Exception e) { this.ProcessServiceException("OnApply", record, e); serviceException = e; } record.CompletedApply(serviceException); return; }
public void Unlock(LogicalLogRecord record) { Utility.Assert(record.IsApplied == true, "record.IsApplied == true"); Utility.Assert(this.recoveredOrCopiedCheckpointLsn.Value != LogicalSequenceNumber.InvalidLsn, "recoveredOrCopiedCheckpointLsn must not be -1"); // If flush fails, the records are processed immediately, after which the processing can complete (before waiting for unlocks) if (record.FlushException == null) { Utility.Assert( roleContextDrainState.DrainingStream != DrainingStream.Invalid, "this.DrainingStream != DrainingStream.Invalid during unlock for record lsn:{0} psn:{1}", record.Lsn, record.Psn); } BeginTransactionOperationLogRecord beginTransactionRecord; try { OperationLogRecord operationRecord; switch (record.RecordType) { case LogRecordType.BeginTransaction: beginTransactionRecord = (BeginTransactionOperationLogRecord)record; if (beginTransactionRecord.IsSingleOperationTransaction) { if (beginTransactionRecord.Lsn > this.recoveredOrCopiedCheckpointLsn.Value) { var operationContext = beginTransactionRecord.ResetOperationContext(); if (operationContext != null) { this.stateManager.Unlock(operationContext); } } this.FireCommitNotification((ITransaction)beginTransactionRecord.Transaction); } break; case LogRecordType.Operation: operationRecord = (OperationLogRecord)record; if (operationRecord.Transaction.IsAtomicOperation == true) { if (operationRecord.Lsn > this.recoveredOrCopiedCheckpointLsn.Value) { var operationContext = operationRecord.ResetOperationContext(); if (operationContext != null) { this.stateManager.Unlock(operationContext); } } } break; case LogRecordType.EndTransaction: var endTransactionRecord = (EndTransactionLogRecord)record; if (endTransactionRecord.Lsn > this.recoveredOrCopiedCheckpointLsn.Value) { TransactionLogRecord transactionRecord = endTransactionRecord; do { transactionRecord = transactionRecord.ParentTransactionRecord; beginTransactionRecord = transactionRecord as BeginTransactionOperationLogRecord; } while (beginTransactionRecord == null); Utility.Assert( (beginTransactionRecord != null) && (LogRecord.IsInvalidRecord(beginTransactionRecord) == false), "(beginTransactionRecord != null) && (TransactionLogRecord.IsInvalidRecord(beginTransactionRecord) == false)"); var operationContext = beginTransactionRecord.ResetOperationContext(); if (operationContext != null) { this.stateManager.Unlock(operationContext); } transactionRecord = beginTransactionRecord; do { transactionRecord = transactionRecord.ChildTransactionRecord; Utility.Assert( (transactionRecord != null) && (LogRecord.IsInvalidRecord(transactionRecord) == false), "(transactionRecord != null) && (TransactionLogRecord.IsInvalidRecord(transactionRecord) == false)"); if (transactionRecord == endTransactionRecord) { break; } operationRecord = (OperationLogRecord)transactionRecord; operationContext = operationRecord.ResetOperationContext(); if (operationContext != null) { this.stateManager.Unlock(operationContext); } } while (true); } else { TransactionLogRecord transactionRecord = endTransactionRecord; do { var parentRecord = transactionRecord.ParentTransactionRecord; if (parentRecord == null) { break; } transactionRecord = parentRecord; } while (true); Utility.Assert( (endTransactionRecord.IsEnlistedTransaction == false) || (transactionRecord is BeginTransactionOperationLogRecord), "(endTransactionRecord.IsEnlistedTransaction {0} == false) || " + "(transactionRecord is BeginTransactionLogRecord {1})", endTransactionRecord.IsEnlistedTransaction, transactionRecord.RecordType); if (transactionRecord != endTransactionRecord) { do { transactionRecord = transactionRecord.ChildTransactionRecord; Utility.Assert( (transactionRecord != null) && (LogRecord.IsInvalidRecord(transactionRecord) == false), "(transactionRecord != null) && (TransactionLogRecord.IsInvalidRecord(transactionRecord) == false)"); if (transactionRecord == endTransactionRecord) { break; } } while (true); } } if (endTransactionRecord.IsCommitted == true) { this.FireCommitNotification((ITransaction)endTransactionRecord.Transaction); } break; case LogRecordType.Backup: var backupLogRecord = (BackupLogRecord)record; Utility.Assert(backupManager != null, "Backupmanager must not be null in UnlockCallbackManager"); backupManager.LastCompletedBackupLogRecord = backupLogRecord; break; default: Utility.CodingError("Unexpected record type {0}", record.RecordType); break; } } catch (Exception e) { this.ProcessServiceException("Unlockcallback", record, e); } this.ProcessedLogicalRecord(record); return; }
internal async Task <PhysicalLogReader> OpenWithRestoreFileAsync( ReplicaOpenMode openMode, FabricPerformanceCounterSetInstance perfCounterInstance, IList <string> backupLogFilePathList, int flushEveryNKB) { this.LogicalLog = await this.CreateLogFileAsync(openMode == ReplicaOpenMode.New, CancellationToken.None).ConfigureAwait(false); // 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.WritePosition == 0, "{0}: this.logicalLog.Length: {1} this.logicalLog.WritePosition: {2}", this.tracer.Type, this.LogicalLog.Length, this.LogicalLog.WritePosition); } using (PhysicalLogWriter logWriter = new PhysicalLogWriter( this.LogicalLog, this.emptyCallbackManager, this.Tracer, this.MaxWriteCacheSizeInMB, this.IncomingBytesRateCounterWriter, this.LogFlushBytesRateCounterWriter, this.BytesPerFlushCounterWriter, this.AvgFlushLatencyCounterWriter, this.AvgSerializationLatencyCounterWriter, true)) { LogRecord record = null; LogRecordsMap logRecordsMap = null; long bufferedRecordsSizeBytes = -1; long backupRecordIndex = 0; foreach (string backupLogFilePath in backupLogFilePathList) { BackupLogFile backupLogFile = await BackupLogFile.OpenAsync( backupLogFilePath, CancellationToken.None).ConfigureAwait(false); using (var backupLogEnumerator = backupLogFile.GetAsyncEnumerator()) { if (logRecordsMap == null) { bool hasFirstRecord = await backupLogEnumerator.MoveNextAsync(CancellationToken.None).ConfigureAwait(false); Utility.Assert(hasFirstRecord, "{0}: Backup must include at least six records.", this.tracer.Type); // If the log is being restored. // First record must be a indexing log record. Flush it. LogRecord firstRecordFromBackupLog = backupLogEnumerator.Current; Utility.Assert( null != firstRecordFromBackupLog, "{0}: BackupLogEnumerator will never return null", this.tracer.Type); Utility.Assert( false == LogRecord.IsInvalidRecord(firstRecordFromBackupLog), "{0}: First record read from the backup log cannot be invalid", this.tracer.Type); IndexingLogRecord logHead = firstRecordFromBackupLog as IndexingLogRecord; Utility.Assert( null != logHead, "{0}: First record read from the backup log must be indexing log record: Type: {1} LSN: {2} PSN: {3} Position: {4}", this.tracer.Type, firstRecordFromBackupLog.RecordType, firstRecordFromBackupLog.Lsn.LSN, firstRecordFromBackupLog.Psn.PSN, firstRecordFromBackupLog.RecordPosition); logRecordsMap = new LogRecordsMap(logHead, this.Tracer); logRecordsMap.ProcessLogRecord(logHead); bufferedRecordsSizeBytes = logWriter.InsertBufferedRecord(logHead); // Note that logHead.PreviousPhysicalRecord is an InvalidLogRecord. backupRecordIndex++; FabricEvents.Events.RestoreOperationAsync( this.Tracer.Type, logHead.RecordType.ToString(), backupRecordIndex, logHead.Lsn.LSN, logHead.Psn.PSN, long.MaxValue); } while (await backupLogEnumerator.MoveNextAsync(CancellationToken.None).ConfigureAwait(false)) { record = backupLogEnumerator.Current; logRecordsMap.ProcessLogRecord(record); // Note: Function inserts a record to the buffer where it waits to be flushed and return // the new size of the whole buffer. bufferedRecordsSizeBytes = logWriter.InsertBufferedRecord(record); backupRecordIndex++; FabricEvents.Events.RestoreOperationAsync( this.Tracer.Type, record.RecordType.ToString(), backupRecordIndex, record.Lsn.LSN, record.Psn.PSN, record.PreviousPhysicalRecord.Psn.PSN); // TODO: Use a backup config for this flush size determination if (bufferedRecordsSizeBytes > flushEveryNKB * 1024) { string intermediateFlushMessage = string.Format( CultureInfo.InvariantCulture, "LogManager: OpenWithRestoreFile (Restore) Intermediate Flush Size: {0} bytes, Index: {1}", bufferedRecordsSizeBytes, backupRecordIndex); await logWriter.FlushAsync(intermediateFlushMessage).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 record.AwaitFlush().ConfigureAwait(false); bufferedRecordsSizeBytes = 0; } } } } // Note: Move the last flush for remaining items out of the loop to avoid unnecessary flush in the // case of each iteration has a small number of un-flushed log records. if (bufferedRecordsSizeBytes > 0) { string flushMessage = string.Format( CultureInfo.InvariantCulture, "LogManager: OpenWithRestoreFile (Restore)"); await logWriter.FlushAsync(flushMessage).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 record.AwaitFlush().ConfigureAwait(false); } } return(new PhysicalLogReader(this)); }