コード例 #1
0
        internal void TruncateTail(ProgressVectorEntry lastProgressVectorEntry)
        {
            var lastIndex = this.vectors.Count - 1;

            Utility.Assert(this.vectors[lastIndex] == lastProgressVectorEntry, "this.vectors[lastIndex] == LastProgressVectorEntry");
            this.vectors.RemoveAt(lastIndex);

            return;
        }
コード例 #2
0
        /// <summary>
        /// Process the update epoch log record.
        /// </summary>
        /// <param name="updateEpochLogRecord">The truncate tail log record to be processed.</param>
        /// <param name="isRecoverableRecord">Is this a recoverable record.</param>
        private void ProcessLogRecord(UpdateEpochLogRecord updateEpochLogRecord, out bool isRecoverableRecord)
        {
            isRecoverableRecord = true;

            Utility.Assert(
                this.LastLogicalSequenceNumber == updateEpochLogRecord.Lsn,
                "{0} this.LastLogicalSequenceNumber ({1}) == updateEpochLogRecord.Lsn ({2})",
                this.tracer.Type,
                this.LastLogicalSequenceNumber,
                updateEpochLogRecord.Lsn);

            var updateEpochRecordEpoch = updateEpochLogRecord.Epoch;

            if (this.CurrentLogTailEpoch < updateEpochRecordEpoch)
            {
                this.CurrentLogTailEpoch = updateEpochRecordEpoch;
            }

            if (Mode.Recovery == this.mode)
            {
                ProgressVectorEntry progressVectorEntry;

                if (updateEpochLogRecord.RecordPosition
                    > this.recoveredLastCompletedBeginCheckpointRecord.RecordPosition)
                {
                    progressVectorEntry = new ProgressVectorEntry(updateEpochLogRecord);
                    if (this.ProgressVector.LastProgressVectorEntry.Epoch < updateEpochRecordEpoch)
                    {
                        this.ProgressVector.Add(progressVectorEntry);
                    }
                    else
                    {
                        var isInserted = this.ProgressVector.Insert(progressVectorEntry);
                        Utility.Assert(
                            isInserted == true,
                            "{0} isInserted ({1}) == true. Incoming: {2} CurrentTail: {3}",
                            this.tracer.Type,
                            isInserted,
                            progressVectorEntry.ToString(),
                            this.ProgressVector.LastProgressVectorEntry.ToString());
                    }
                }
                else
                {
                    progressVectorEntry = this.ProgressVector.Find(updateEpochRecordEpoch);
                    Utility.Assert(
                        (progressVectorEntry != null) && (progressVectorEntry.Lsn == this.LastLogicalSequenceNumber),
                        "{0} (ProgressVectorEntry != null) && (ProgressVectorEntry.LastLogicalSequenceNumber {1} == LSN {2})",
                        this.tracer.Type,
                        this.LastLogicalSequenceNumber,
                        progressVectorEntry.Lsn);
                }
            }
        }
コード例 #3
0
        internal void Add(ProgressVectorEntry progressVectorEntry)
        {
            var lastVector = this.LastProgressVectorEntry;

            Utility.Assert(
                (lastVector == null) || ((lastVector.Epoch < progressVectorEntry.Epoch) && (lastVector.Lsn <= progressVectorEntry.Lsn)),
                "(LastProgressVectorEntry == null) || ((LastProgressVectorEntry.Epoch < ProgressVectorEntry.Epoch) && (LastProgressVectorEntry.Lsn <= ProgressVectorEntry.Lsn)) IsLastVectorNull :{0}",
                lastVector == null);

            this.vectors.Add(progressVectorEntry);
            return;
        }
コード例 #4
0
        internal void Read(BinaryReader br, bool isPhysicalRead)
        {
            var count = br.ReadInt32();

            for (var i = 0; i < count; i++)
            {
                var vector = new ProgressVectorEntry();
                vector.Read(br, isPhysicalRead);
                this.vectors.Add(vector);
            }

            return;
        }
コード例 #5
0
        internal void TruncateHead(ProgressVectorEntry firstProgressVectorEntry)
        {
            for (var i = 0; i < this.vectors.Count; i++)
            {
                if (this.vectors[i].Epoch == firstProgressVectorEntry.Epoch)
                {
                    this.vectors[i] = firstProgressVectorEntry;
                    if (i > 0)
                    {
                        this.vectors.RemoveRange(0, i);
                    }

                    return;
                }
            }

            return;
        }
コード例 #6
0
        internal bool Insert(ProgressVectorEntry progressVectorEntry)
        {
            for (var i = this.vectors.Count - 1; i >= 0; i--)
            {
                var existingVector = this.vectors[i];
                if (existingVector.Epoch == progressVectorEntry.Epoch)
                {
                    Utility.Assert(existingVector.Lsn == progressVectorEntry.Lsn, "existingVector.Lsn == ProgressVectorEntry.Lsn");
                    return(false);
                }
                else if (existingVector.Epoch < progressVectorEntry.Epoch)
                {
                    this.vectors.Insert(i + 1, progressVectorEntry);
                    return(true);
                }

                Utility.Assert(existingVector.Lsn == progressVectorEntry.Lsn, "existingVector.Lsn == ProgressVectorEntry.Lsn");
            }

            // We should never reach here
            this.vectors.Insert(0, progressVectorEntry);
            return(true);
        }
コード例 #7
0
        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);
        }
コード例 #8
0
        private static bool DecrementIndexUntilLeq(ref int index, ProgressVector vector, ProgressVectorEntry comparand)
        {
            do
            {
                if (vector.vectors[index] <= comparand)
                {
                    break;
                }

                // this can happen when the progress vector is trimmed and does not contain the index relative to the comparand
                if (index == 0)
                {
                    return(false);
                }
                --index;
            } while (true);

            return(true);
        }
コード例 #9
0
 public bool IsDataLossBetween(ProgressVectorEntry other)
 {
     return(this.Epoch.DataLossNumber != other.Epoch.DataLossNumber);
 }
コード例 #10
0
 // progress vector entries are stutter of each other if they represent a same data loss and LSN number and they don't have any other entry between them.
 // configuration numbers could be (and MUST be) different as we have no repeating progress vector entries in the list.
 // caller must make sure that p1 and p2 are indeed neighbors of each other in the progress vector list and only after that confirmation, call this function for stutter check.
 private bool EntriesStutter(ProgressVectorEntry p1, ProgressVectorEntry p2)
 {
     return(p1.Epoch.DataLossNumber == p2.Epoch.DataLossNumber && p1.Lsn.LSN == p2.Lsn.LSN);
 }