/// <summary> /// Primary: Process the logical log record. /// </summary> /// <param name="record"></param> /// <returns></returns> /// <remarks> /// Wait for the logical record to be flushed and replicated. /// If it is a barrier, increase stable lsn. /// If it is a operation record, unlock it. /// </remarks> private async Task ProcessLogicalRecordOnPrimaryAsync(TransactionLogRecord record) { Exception exception = null; this.ReplicateAndLogLogicalRecord(record); try { // Wait for the record to be flushed and replicated await Task.WhenAll(record.AwaitApply(), LoggingReplicator.AwaitReplication(record, this.replicatedLogManager)).ConfigureAwait(false); } catch (Exception e) { exception = e; } finally { this.InvokeUnlockCallback(record); } // Throw TransactionFaultedException if there has been any failure during replication and apply awaits. if (exception != null) { throw new TransactionFaultedException(exception.Message); } return; }
private void ReadPrivate(BinaryReader br, bool isPhysicalRead) { var logicalStartingPosition = br.BaseStream.Position; var logicalSizeOfSection = br.ReadInt32(); var logicalEndPosition = logicalStartingPosition + logicalSizeOfSection; this.transaction = TransactionBase.CreateTransaction(br.ReadInt64(), true); // Jump to the end of the section ignoring fields that are not understood. Utility.Assert(logicalEndPosition >= br.BaseStream.Position, "Could not have read more than section size."); br.BaseStream.Position = logicalEndPosition; if (isPhysicalRead) { // Metadata section. var physicalStartPosition = br.BaseStream.Position; var physicalSectionSize = br.ReadInt32(); var physicalEndPosition = physicalStartPosition + physicalSectionSize; this.parentTransactionRecordOffset = br.ReadUInt64(); if (this.parentTransactionRecordOffset == 0) { this.parentTransactionRecord = null; } // Jump to the end of the section ignoring fields that are not understood. Utility.Assert(physicalEndPosition >= br.BaseStream.Position, "Could not have read more than section size."); br.BaseStream.Position = physicalEndPosition; } this.UpdateApproximateDiskSize(); }
private long Append( LogicalLogRecord record, TransactionManager transactionsManager, bool isPrimary) { long bufferedBytes = 0; TransactionLogRecord txLogRecord = record as TransactionLogRecord; TaskCompletionSource <object> lsnOrderingTcsToSignal = null; if (txLogRecord != null) { Utility.Assert(transactionsManager != null, "transactionsManager should not be null in AppendOnPrimary"); transactionsManager.CreateTransactionState(this.CurrentLogTailEpoch, txLogRecord); } lock (this.lsnOrderingLock) { bufferedBytes = LsnOrderingInsert(record, isPrimary, out lsnOrderingTcsToSignal); } if (lsnOrderingTcsToSignal != null) { // Do this outside the lock above and also after setting the this.lsnorderingtcs to NULL as this set result can end up invoking a completion to run on the same thread, // which ends up invoking another call to Append. This happened in a unit test on the UpdateEpoch code path on primary bool result = lsnOrderingTcsToSignal.TrySetResult(null); Utility.Assert(result == true, "ReplicatedLogManager: failed to set result on lsn ordering tcs"); } return(bufferedBytes); }
protected TransactionLogRecord() : base() { this.transaction = TransactionBase.InvalidTransaction; this.parentTransactionRecordOffset = InvalidLogicalRecordOffset; this.parentTransactionRecord = InvalidTransactionLogRecord; this.childTransactionRecord = new WeakReference <TransactionLogRecord>(InvalidTransactionLogRecord); this.isEnlistedTransaction = false; }
internal TransactionLogRecord(LogRecordType recordType, ulong recordPosition, long lsn) : base(recordType, recordPosition, lsn) { this.transaction = TransactionBase.InvalidTransaction; this.parentTransactionRecordOffset = InvalidLogicalRecordOffset; this.parentTransactionRecord = InvalidTransactionLogRecord; this.childTransactionRecord = new WeakReference <TransactionLogRecord>(InvalidTransactionLogRecord); this.isEnlistedTransaction = false; }
internal TransactionLogRecord( LogRecordType recordType, TransactionBase transaction, TransactionLogRecord parentLogicalRecord) : base(recordType) { this.transaction = transaction; this.parentTransactionRecordOffset = InvalidLogicalRecordOffset; this.parentTransactionRecord = parentLogicalRecord; this.childTransactionRecord = new WeakReference <TransactionLogRecord>(InvalidTransactionLogRecord); this.isEnlistedTransaction = false; this.UpdateApproximateDiskSize(); }
private void ProcessLogicalRecordOnPrimary(TransactionLogRecord record) { this.ReplicateAndLogLogicalRecord(record); Task.WhenAll(record.AwaitApply(), LoggingReplicator.AwaitReplication(record, this.replicatedLogManager)).IgnoreException().ContinueWith( task => { // Simply retrieve task exception to mark it as handled if (task.Exception != null) { Utility.Assert(task.IsFaulted == true, "task.IsFaulted == true"); } this.InvokeUnlockCallback(record); }).IgnoreExceptionVoid(); }
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; }
public void CreateTransactionState( Epoch logTailEpoch, TransactionLogRecord record) { switch (record.RecordType) { case LogRecordType.BeginTransaction: var beginTxRecord = record as BeginTransactionOperationLogRecord; this.beginTransactionOperationRatePerformanceCounterWriter.Increment(); if (beginTxRecord.IsSingleOperationTransaction) { this.transactionCommitRatePerformanceCounterWriter.Increment(); FabricEvents.Events.AcceptSingleOperationTransaction( tracer.Type, beginTxRecord.Transaction.Id, record.Lsn.LSN); record.Transaction.CommitSequenceNumber = record.Lsn.LSN; } else { FabricEvents.Events.AcceptBeginTransaction( tracer.Type, beginTxRecord.Transaction.Id, record.Lsn.LSN); } beginTxRecord.RecordEpoch = logTailEpoch; this.transactionMap.CreateTransaction(beginTxRecord); break; case LogRecordType.Operation: var dataRecord = record as OperationLogRecord; if (dataRecord.Transaction.IsAtomicOperation || dataRecord.IsRedoOnly) { this.addAtomicOperationRatePerformanceCounterWriter.Increment(); dataRecord.Transaction.CommitSequenceNumber = record.Lsn.LSN; } else { this.addOperationRatePerformanceCounterWriter.Increment(); } FabricEvents.Events.AcceptOperation(tracer.Type, dataRecord.Transaction.Id, record.Lsn.LSN); this.transactionMap.AddOperation(dataRecord); Utility.Assert( (dataRecord.IsEnlistedTransaction == true) || (dataRecord.Lsn <= this.recoveredOrCopiedCheckpointLsn.Value), "(record.IsEnlistedTransaction == true) || (record.LastLogicalSequenceNumber <= this.recoveredOrCopiedCheckpointLsn). record LSN :{0} and recoverd or copied checkpoint lsn:{1}. record.InEnlistedTx :{2}", dataRecord.Lsn, recoveredOrCopiedCheckpointLsn, dataRecord.IsEnlistedTransaction); break; case LogRecordType.EndTransaction: var endTxRecord = record as EndTransactionLogRecord; if (endTxRecord.IsCommitted) { this.transactionCommitRatePerformanceCounterWriter.Increment(); endTxRecord.Transaction.CommitSequenceNumber = record.Lsn.LSN; } else { this.transactionAbortRatePerformanceCounterWriter.Increment(); } FabricEvents.Events.AcceptEndTransaction( tracer.Type, endTxRecord.Transaction.Id, record.Lsn.LSN, endTxRecord.IsCommitted); this.transactionMap.CompleteTransaction(endTxRecord); Utility.Assert( (endTxRecord.IsEnlistedTransaction == true) || (record.Lsn <= this.recoveredOrCopiedCheckpointLsn.Value), "(record.IsEnlistedTransaction == true) || (record.LastLogicalSequenceNumber <= this.recoveredOrCopiedCheckpointLsn). record LSN :{0} and recoverd or copied checkpoint lsn:{1}. record.InEnlistedTx :{2}", record.Lsn, this.recoveredOrCopiedCheckpointLsn.Value.LSN, endTxRecord.IsEnlistedTransaction); break; default: Utility.CodingError("Unexpected record type in TransactionsManager CreateTransactionState {0}", record.RecordType); break; } }
private void InvokeUnlockCallback(TransactionLogRecord record) { this.unlockCallbackManager.Unlock(record); }
private void ReplicateAndLogLogicalRecord(TransactionLogRecord record) { this.InitiateLogicalRecordProcessingOnPrimary(record); return; }