예제 #1
0
        private async Task FlushTask(object initiatingTcs)
        {
            var flushingTasks = new List <TaskCompletionSource <object> >();

            flushingTasks.Add((TaskCompletionSource <object>)initiatingTcs);

            var isFlushTask        = true;
            var flushWatch         = Stopwatch.StartNew();
            var serializationWatch = Stopwatch.StartNew();

            do
            {
                TaskCompletionSource <object> notificationTcs;

                try
                {
                    Utility.Assert(this.loggingException == null, "this.loggingException == null");

                    var   numberOfLatencySensitiveRecords = 0;
                    ulong numberOfBytes = 0;
                    var   startingPsn   = this.flushingRecords[0].Psn;

                    FabricEvents.Events.PhysicalLogWriterFlushStart(
                        this.tracer.Type,
                        this.flushingRecords.Count,
                        startingPsn.PSN);

                    // Enable group commit as we are about to issue IO
                    // Operations above this line must not throw.
                    notificationTcs = Interlocked.Exchange <TaskCompletionSource <object> >(
                        ref this.flushNotificationTcs,
                        FlushPendingTcs);

                    flushWatch.Reset();
                    serializationWatch.Reset();

                    foreach (var record in this.flushingRecords)
                    {
                        serializationWatch.Start();
                        var operationData = this.WriteRecord(record, numberOfBytes);
#if DEBUG
                        ReplicatedLogManager.ValidateOperationData(operationData, "Log Write for " + record.RecordType);
#endif
                        var logicalRecord = record as LogicalLogRecord;
                        if (logicalRecord != null && logicalRecord.IsLatencySensitiveRecord == true)
                        {
                            ++numberOfLatencySensitiveRecords;
                        }

                        serializationWatch.Stop();

                        this.avgSerializationLatencyCounterWriter.IncrementBy(serializationWatch);

                        flushWatch.Start();
                        foreach (var arraySegment in operationData)
                        {
                            numberOfBytes += (ulong)arraySegment.Count;
                            await
                            this.logicalLogStream.AppendAsync(
                                arraySegment.Array,
                                arraySegment.Offset,
                                arraySegment.Count,
                                CancellationToken.None).ConfigureAwait(false);
                        }
                        flushWatch.Stop();
                    }

                    Utility.Assert(notificationTcs == FlushPermittedTcs, "NotificationTcs == FlushPermittedTcs");

                    flushWatch.Start();
                    await this.logicalLogStream.FlushWithMarkerAsync(CancellationToken.None).ConfigureAwait(false);

                    flushWatch.Stop();

                    this.UpdateWriteStats(flushWatch, numberOfBytes);

                    var lastWrittenTailRecord = this.flushingRecords[this.flushingRecords.Count - 1];

                    // Save the new log tail position and no need to interlock as single
                    // instruction 64-bit writes are atomic on 64-bit machines and there
                    // can only be one thread here
                    this.currentLogTailPosition += numberOfBytes;
                    this.currentLogTailRecord    = lastWrittenTailRecord;
                    this.bytesPerFlushCounterWriter.IncrementBy((long)numberOfBytes);

                    if (flushWatch.ElapsedMilliseconds > 3000)
                    {
                        FabricEvents.Events.PhysicalLogWriterFlushEndWarning(
                            this.tracer.Type,
                            numberOfBytes,
                            this.flushingRecords.Count,
                            numberOfLatencySensitiveRecords,
                            flushWatch.ElapsedMilliseconds,
                            serializationWatch.ElapsedMilliseconds,
                            (double)this.writeSpeedBytesPerSecondSum / MovingAverageHistory,
                            (double)this.runningLatencySumMs / MovingAverageHistory,
                            this.logicalLogStream.WritePosition - (long)numberOfBytes);
                    }
                    else
                    {
                        FabricEvents.Events.PhysicalLogWriterFlushEnd(
                            this.tracer.Type,
                            numberOfBytes,
                            this.flushingRecords.Count,
                            numberOfLatencySensitiveRecords,
                            flushWatch.ElapsedMilliseconds,
                            serializationWatch.ElapsedMilliseconds,
                            (double)this.writeSpeedBytesPerSecondSum / MovingAverageHistory,
                            (double)this.runningLatencySumMs / MovingAverageHistory,
                            this.logicalLogStream.WritePosition - (long)numberOfBytes);
                    }
                }
                catch (Exception e)
                {
                    this.loggingException = e;
                }
                finally
                {
                    try
                    {
                        // Disable group commit as there is no pending IO
                        notificationTcs = this.FlushCompleted();

                        // GopalK: The order of the following instructions is very important.
                        // It is important to process flushed records first before
                        // waking up flush waiters
                        var flushedTasks   = flushingTasks;
                        var flushedRecords = new LoggedRecords(this.flushingRecords, this.loggingException);
                        this.ProcessFlushedRecords(flushedRecords);

                        isFlushTask = this.ProcessFlushCompletion(flushedRecords, ref flushedTasks, out flushingTasks);

                        this.WakeupFlushWaiters(flushedTasks);
                        if ((isFlushTask == true) && (this.loggingException != null))
                        {
                            this.FailedFlushTask(flushingTasks);
                            isFlushTask = false;
                        }
                    }
                    catch (Exception e)
                    {
                        int innerHResult     = 0;
                        var exception        = Utility.FlattenException(e, out innerHResult);
                        var exceptionMessage =
                            string.Format(
                                CultureInfo.InvariantCulture,
                                "PhysicalLogWriter::FlushTask() - Hit an exception, Type: {0}, Message: {1}, HResult: 0x{2:X8}, Stack Trace: {3}",
                                exception.GetType().ToString(),
                                exception.Message,
                                exception.HResult != 0 ? exception.HResult : innerHResult,
                                exception.StackTrace);
                        FabricEvents.Events.Warning(this.tracer.Type, exceptionMessage);

                        Utility.Assert(false, exceptionMessage);
                        throw;
                    }
                }
            } while (isFlushTask == true);

            return;
        }
예제 #2
0
        internal static void Assert(bool condition, string format, TruncateHeadLogRecord param1, LogRecord param2)
        {
            if (condition == false)
            {
                var failFastMessage = string.Format(System.Globalization.CultureInfo.InvariantCulture, format, param1, param2);
                FailFast(failFastMessage);

                // AMW - Force break into debugger for ease of debugging
                Debugger.Break();
            }
        }
예제 #3
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);
        }
예제 #4
0
        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;
        }
예제 #5
0
 public void TruncateTail(LogRecord currentRecord)
 {
     this.LastAppliedBarrierRecord = currentRecord;
 }
예제 #6
0
        public async Task <long> CommitTransactionAsync(Transaction transaction)
        {
            LogRecord endTransactionRecord = await this.EndTransactionAsync(transaction, true, true).ConfigureAwait(false);

            return(endTransactionRecord.Lsn.LSN);
        }
예제 #7
0
        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;
        }
예제 #8
0
 internal LoggedRecords(LogRecord record, Exception exception)
 {
     this.records = new List <LogRecord>();
     this.records.Add(record);
     this.exception = exception;
 }
예제 #9
0
        public RecordProcessingMode IdentifyProcessingModeForRecord(LogRecord record)
        {
            var stream = this.roleContextDrainState.DrainingStream;

            Utility.Assert(
                stream != DrainingStream.Invalid || record is UpdateEpochLogRecord || record is PhysicalLogRecord,
                @"Draining stream should not be invalid or record should be of type epoch or physical log record.
                            DrainingStream: {0} RecordType: {1}",
                stream, record.RecordType);

            if (LoggingReplicator.IsBarrierRecord(record))
            {
                this.LastAppliedBarrierRecord = record;
            }

            RecordProcessingMode processingMode;

            switch (record.RecordType)
            {
            case LogRecordType.EndCheckpoint:
            case LogRecordType.TruncateTail:
            case LogRecordType.Indexing:
            case LogRecordType.UpdateEpoch:
            case LogRecordType.Information:
            case LogRecordType.CompleteCheckpoint:
                processingMode = RecordProcessingMode.ProcessImmediately;
                break;

            case LogRecordType.Barrier:
                processingMode = (this.roleContextDrainState.ReplicaRole == ReplicaRole.Unknown)
                        ? RecordProcessingMode.ProcessImmediately
                        : RecordProcessingMode.ApplyImmediately;
                break;

            case LogRecordType.TruncateHead:
                processingMode = RecordProcessingMode.ApplyImmediately;
                break;

            case LogRecordType.BeginCheckpoint:
                Utility.Assert(
                    (record.Lsn >= this.recoveredOrCopiedCheckpointLsn.Value) ||
                    (this.roleContextDrainState.ReplicaRole == ReplicaRole.Unknown),
                    "record.LastLogicalSequenceNumber >= this.recoveredOrCopiedCheckpointLsn || "
                    + "(this.replicaRole == ReplicaRole.Unknown)");
                processingMode = (this.roleContextDrainState.ReplicaRole == ReplicaRole.Unknown)
                        ? RecordProcessingMode.ProcessImmediately
                        : RecordProcessingMode.ApplyImmediately;
                break;

            case LogRecordType.BeginTransaction:
            case LogRecordType.Operation:
            case LogRecordType.EndTransaction:
            case LogRecordType.Backup:
                processingMode = RecordProcessingMode.Normal;
                break;

            default:
                Utility.CodingError("Unexpected record type {0}", record.RecordType);
                processingMode = RecordProcessingMode.Normal;
                break;
            }

            return(processingMode);
        }
예제 #10
0
        private async Task DrainCopyStreamAsync(
            IOperationStream copyStream,
            IOperation operation,
            BeginCheckpointLogRecord copiedCheckpointRecord,
            bool renamedCopyLogSuccessfully)
        {
            FabricEvents.Events.DrainStart(this.tracer.Type, "Copy stream: RenamedCopyLogSuccessfully: " + renamedCopyLogSuccessfully);

            var  lastCopiedRecord = LogicalLogRecord.InvalidLogicalLogRecord;
            long copiedRecordNumber = 0, acksOutstanding = 1;

            TaskCompletionSource <object> allOperationsAckedTcs = new TaskCompletionSource <object>();

            try
            {
                if (operation != null)
                {
                    this.roleContextDrainState.OnDrainCopy();

                    do
                    {
                        var data = operation.Data;
#if DEBUG
                        ReplicatedLogManager.ValidateOperationData(data, "DrainCopyStreamAsync LSN: " + operation.SequenceNumber);
#endif
                        lastCopiedRecord = (LogicalLogRecord)LogRecord.FromOperationData(data);

                        await this.LogLogicalRecordOnSecondaryAsync(lastCopiedRecord).ConfigureAwait(false);

                        // After successfully appending the record into the buffer, increment the outstanding ack count
                        var acksRemaining = Interlocked.Increment(ref acksOutstanding);

                        FabricEvents.Events.DrainCopyReceive(
                            this.tracer.Type,
                            copiedRecordNumber,
                            lastCopiedRecord.RecordType.ToString(),
                            lastCopiedRecord.Lsn.LSN,
                            acksRemaining);

                        ++copiedRecordNumber;
                        if (this.replicatorSettings.PublicSettings.MaxCopyQueueSize / 2 <= acksRemaining)
                        {
                            FabricEvents.Events.DrainCopyFlush(
                                this.tracer.Type,
                                copiedRecordNumber,
                                lastCopiedRecord.Lsn.LSN,
                                acksRemaining);

                            this.replicatedLogManager.LogManager.FlushAsync("DrainCopyStream.IsFull").IgnoreExceptionVoid();
                        }

                        var capturedOperation = operation;
                        var capturedRecord    = lastCopiedRecord;
                        if (copiedCheckpointRecord == null)
                        {
                            copiedCheckpointRecord = this.replicatedLogManager.LastInProgressCheckpointRecord;
                            if (copiedCheckpointRecord != null)
                            {
                                Utility.Assert(
                                    copiedCheckpointRecord.Lsn == this.recoveredOrCopiedCheckpointLsn.Value,
                                    "copiedCheckpointRecordLsn {0} == recoveredOrCopiedCheckpointLsn {1}",
                                    copiedCheckpointRecord.Lsn,
                                    this.recoveredOrCopiedCheckpointLsn.Value);
                            }
                        }

                        // If pumped the last operation in the copy stream (indicated by copiedUptoLsn), rename the copy log if this was a full copy
                        // as we are guranteed that the replica has all the data needed to be promoted to an active secondary and we could not have lost any state
                        if (copiedCheckpointRecord != null &&
                            copiedCheckpointRecord != BeginCheckpointLogRecord.InvalidBeginCheckpointLogRecord &&
                            lastCopiedRecord.Lsn == this.copiedUptoLsn &&
                            renamedCopyLogSuccessfully == false) // Copied UE record could have same LSN, so this condition is needed
                        {
                            await this.checkpointManager.CompleteFirstCheckpointOnIdleAndRenameLog(copiedCheckpointRecord, this.copiedUptoLsn.LSN).ConfigureAwait(false);

                            renamedCopyLogSuccessfully = true;
                        }

                        lastCopiedRecord.AwaitFlush().ContinueWith(
                            async task =>
                        {
                            var acksPending = Interlocked.Decrement(ref acksOutstanding);

                            if (task.Exception != null)
                            {
                                // Signal the drain completion task if needed
                                if (acksPending == 0)
                                {
                                    allOperationsAckedTcs.TrySetResult(null);
                                }

                                return;
                            }

                            capturedOperation.Acknowledge();

                            Utility.Assert(acksPending >= 0, "acksPending {0} >= 0", acksPending);

                            if (acksPending == 0)
                            {
                                allOperationsAckedTcs.TrySetResult(null);
                            }

                            FabricEvents.Events.DrainCopyNoise(
                                this.tracer.Type,
                                capturedRecord.Lsn.LSN,
                                acksPending);

                            await capturedRecord.AwaitApply().ConfigureAwait(false);
                        }).IgnoreExceptionVoid();

                        var drainTask = copyStream.GetOperationAsync(CancellationToken.None);
                        if (drainTask.IsCompleted == false)
                        {
                            // GopalK: Currently, we cannot wait for copy to finish because copy might get
                            // abandoned if the primary fails and the product waits for pending
                            // copy operations to get acknowledged before electing a new primary
                            this.replicatedLogManager.LogManager.FlushAsync("DrainCopyStream.IsEmpty").IgnoreExceptionVoid();
                            await drainTask.ConfigureAwait(false);
                        }

                        operation = drainTask.GetAwaiter().GetResult();
                    } while (operation != null);
                }
            }

            // This finally block ensures that before we continue, we cancel the first full copy checkpoint during full build
            // Without having this, it is possible that the above code throws out of this method and any lifecycle API like close might get stuck because
            // there is a pending checkpoint that is not yet fully processed
            finally
            {
                // If the pump prematurely finishes for any reason, it means the copy log cannot be renamed
                if (copiedCheckpointRecord != null &&
                    copiedCheckpointRecord != BeginCheckpointLogRecord.InvalidBeginCheckpointLogRecord &&
                    renamedCopyLogSuccessfully == false)
                {
                    await this.checkpointManager.CancelFirstCheckpointOnIdleDueToIncompleteCopy(copiedCheckpointRecord, this.copiedUptoLsn.LSN);
                }
            }

            await this.replicatedLogManager.FlushInformationRecordAsync(
                InformationEvent.CopyFinished,
                closeLog : false,
                flushInitiator : "DrainCopyStream.IsFinished").ConfigureAwait(false);

            // Awaiting processing of this record,
            // ensures that all operations in the copystream must have been applied Before we complete the drainComplationTcs.
            await this.replicatedLogManager.LastInformationRecord.AwaitProcessing().ConfigureAwait(false);

            await this.recordsProcessor.WaitForLogicalRecordsProcessingAsync().ConfigureAwait(false);

            var acksOpen = Interlocked.Decrement(ref acksOutstanding);
            Utility.Assert(acksOpen >= 0, "acksOpen {0} >= 0", acksOpen);
            if (acksOpen != 0)
            {
                // wait for all the callbacks above to finish running and acknowleding
                await allOperationsAckedTcs.Task.ConfigureAwait(false);
            }

            Utility.Assert(acksOutstanding == 0, "acksOutstanding == 0");

#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.DrainCompleted(
                this.tracer.Type,
                "Copy",
                "Completed",
                copiedRecordNumber,
                (uint)lastCopiedRecord.RecordType,
                lastCopiedRecord.Lsn.LSN,
                lastCopiedRecord.Psn.PSN,
                lastCopiedRecord.RecordPosition);
#endif
        }
예제 #11
0
        private async Task DrainReplicationStreamAsync(IOperationStream replicationStream)
        {
            FabricEvents.Events.DrainStart(this.tracer.Type, "Replication stream");

            TaskCompletionSource <object> allOperationsAckedTcs = new TaskCompletionSource <object>();
            var  lastReplicatedRecord = LogicalLogRecord.InvalidLogicalLogRecord;
            long replicatedRecordNumber = 0, acksOutstanding = 1, bytesOutstanding = 0;

            this.roleContextDrainState.OnDrainReplication();

            do
            {
                var drainTask = replicationStream.GetOperationAsync(CancellationToken.None);
                if (drainTask.IsCompleted == false)
                {
                    this.replicatedLogManager.LogManager.FlushAsync("DrainReplicationStream.IsEmpty").IgnoreExceptionVoid();
                    await drainTask.ConfigureAwait(false);
                }

                var operation = drainTask.GetAwaiter().GetResult();
                if (operation != null)
                {
                    var data = operation.Data;
#if DEBUG
                    ReplicatedLogManager.ValidateOperationData(data, "DrainReplicationStream LSN: " + operation.SequenceNumber);
#endif
                    lastReplicatedRecord     = (LogicalLogRecord)LogRecord.FromOperationData(data);
                    lastReplicatedRecord.Lsn = new LogicalSequenceNumber(operation.SequenceNumber);

                    await this.LogLogicalRecordOnSecondaryAsync(lastReplicatedRecord).ConfigureAwait(false);

                    var acksRemaining = Interlocked.Increment(ref acksOutstanding);

                    FabricEvents.Events.DrainReplicationReceive(
                        this.tracer.Type,
                        replicatedRecordNumber,
                        (uint)lastReplicatedRecord.RecordType,
                        lastReplicatedRecord.Lsn.LSN,
                        acksRemaining);

                    ++replicatedRecordNumber;

                    long operationSize = Utility.GetOperationSize(data);

                    var bytesRemaining = Interlocked.Add(ref bytesOutstanding, operationSize);
                    if (((this.replicatorSettings.PublicSettings.MaxSecondaryReplicationQueueSize / 2 <= acksRemaining) ||
                         ((this.replicatorSettings.PublicSettings.MaxSecondaryReplicationQueueMemorySize > 0) &&
                          (this.replicatorSettings.PublicSettings.MaxSecondaryReplicationQueueMemorySize / 2 <= bytesRemaining))) ||
                        ((this.replicatorSettings.PublicSettings.MaxPrimaryReplicationQueueSize / 2 <= acksRemaining) ||
                         ((this.replicatorSettings.PublicSettings.MaxPrimaryReplicationQueueMemorySize > 0) &&
                          (this.replicatorSettings.PublicSettings.MaxPrimaryReplicationQueueMemorySize / 2 <= bytesRemaining))))
                    {
                        FabricEvents.Events.DrainReplicationFlush(
                            this.tracer.Type,
                            replicatedRecordNumber,
                            lastReplicatedRecord.Lsn.LSN,
                            acksRemaining,
                            bytesRemaining);

                        this.replicatedLogManager.LogManager.FlushAsync("DrainReplicationStream.IsFull").IgnoreExceptionVoid();
                    }

                    var capturedOperation = operation;
                    var capturedRecord    = lastReplicatedRecord;
                    lastReplicatedRecord.AwaitFlush().IgnoreException().ContinueWith(
                        async task =>
                    {
                        var acksPending = Interlocked.Decrement(ref acksOutstanding);

                        if (task.Exception != null)
                        {
                            // Signal the drain completion task if needed
                            if (acksPending == 0)
                            {
                                allOperationsAckedTcs.TrySetResult(null);
                            }

                            return;
                        }

                        var bytesPending = Interlocked.Add(ref bytesOutstanding, -operationSize);
                        Utility.Assert(
                            (acksPending >= 0) && (bytesPending >= 0),
                            "(acksPending >= 0) && (bytesPending >= 0)");

                        if (acksPending == 0)
                        {
                            allOperationsAckedTcs.TrySetResult(null);
                        }

                        capturedOperation.Acknowledge();

                        FabricEvents.Events.DrainReplicationNoise(
                            this.tracer.Type,
                            capturedRecord.Lsn.LSN,
                            acksPending,
                            bytesPending);

                        await capturedRecord.AwaitApply().ConfigureAwait(false);
                    }).IgnoreExceptionVoid();
                }
                else
                {
                    await this.replicatedLogManager.FlushInformationRecordAsync(
                        InformationEvent.ReplicationFinished,
                        closeLog : false,
                        flushInitiator : "DrainReplicationstream.IsFinished").ConfigureAwait(false);

                    await this.replicatedLogManager.LastInformationRecord.AwaitProcessing().ConfigureAwait(false);

                    await this.recordsProcessor.WaitForLogicalRecordsProcessingAsync().ConfigureAwait(false);

                    var acksPending = Interlocked.Decrement(ref acksOutstanding);
                    Utility.Assert(acksPending >= 0, "acksPending >= 0");
                    if (acksPending != 0)
                    {
                        await allOperationsAckedTcs.Task.ConfigureAwait(false);
                    }

                    Utility.Assert(acksOutstanding == 0, "acksOutstanding == 0");
                    break;
                }
            } while (true);

#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.DrainCompleted(
                this.tracer.Type,
                "Replication",
                "Completed",
                replicatedRecordNumber,
                (uint)lastReplicatedRecord.RecordType,
                lastReplicatedRecord.Lsn.LSN,
                lastReplicatedRecord.Psn.PSN,
                lastReplicatedRecord.RecordPosition);
#endif
        }
예제 #12
0
        /// <summary>
        /// Processes the log record.
        /// </summary>
        /// <param name="logRecord">The log record to be processed.</param>
        /// <param name="isRecoverableRecord">Is this a recoverable record.</param>
        public void ProcessLogRecord(LogRecord logRecord, out bool isRecoverableRecord)
        {
            isRecoverableRecord = true;

            var physicalLogRecord = logRecord as PhysicalLogRecord;

            if (physicalLogRecord != null)
            {
                this.ProcessLogRecord(physicalLogRecord);
            }

            switch (logRecord.RecordType)
            {
            // Transaction related log records
            case LogRecordType.BeginTransaction:
                var beginTransactionRecord =
                    (BeginTransactionOperationLogRecord)logRecord;
                this.ProcessLogRecord(beginTransactionRecord, out isRecoverableRecord);
                break;

            case LogRecordType.Operation:
                var operationLogRecord = (OperationLogRecord)logRecord;
                this.ProcessLogRecord(operationLogRecord, out isRecoverableRecord);
                break;

            case LogRecordType.EndTransaction:
                var endTransactionRecord = (EndTransactionLogRecord)logRecord;
                this.ProcessLogRecord(endTransactionRecord, out isRecoverableRecord);
                break;

            case LogRecordType.Barrier:
                var barrierRecord = (BarrierLogRecord)logRecord;
                this.ProcessLogRecord(barrierRecord, out isRecoverableRecord);
                break;

            // Replicated logical record.
            case LogRecordType.Backup:
                var backupLogRecord = (BackupLogRecord)logRecord;
                this.ProcessLogRecord(backupLogRecord, out isRecoverableRecord);
                break;

            // Not replicated logical log record.
            case LogRecordType.UpdateEpoch:
                var updateEpochLogRecord = (UpdateEpochLogRecord)logRecord;
                this.ProcessLogRecord(updateEpochLogRecord, out isRecoverableRecord);
                break;

            // Checkpoint cases
            case LogRecordType.BeginCheckpoint:
                var beginCheckpointRecord = (BeginCheckpointLogRecord)logRecord;
                this.ProcessLogRecord(beginCheckpointRecord, out isRecoverableRecord);
                break;

            case LogRecordType.EndCheckpoint:
                var endCheckpointLogRecord = (EndCheckpointLogRecord)logRecord;
                this.ProcessLogRecord(endCheckpointLogRecord, out isRecoverableRecord);
                break;

            case LogRecordType.CompleteCheckpoint:
                var completeCheckpointLogRecord = (CompleteCheckpointLogRecord)logRecord;
                this.ProcessLogRecord(completeCheckpointLogRecord, out isRecoverableRecord);
                break;

            case LogRecordType.TruncateHead:
                var truncateHeadLogRecord = (TruncateHeadLogRecord)logRecord;
                this.ProcessLogRecord(truncateHeadLogRecord, out isRecoverableRecord);
                break;

            case LogRecordType.Indexing:
                var indexingLogRecord = (IndexingLogRecord)logRecord;
                this.ProcessLogRecord(indexingLogRecord, out isRecoverableRecord);
                break;

            case LogRecordType.Information:
                var informationRecord = (InformationLogRecord)logRecord;
                this.ProcessLogRecord(informationRecord, out isRecoverableRecord);
                break;

            case LogRecordType.TruncateTail:
                var truncateTailLogRecord = (TruncateTailLogRecord)logRecord;
                this.ProcessLogRecord(truncateTailLogRecord, out isRecoverableRecord);
                break;

            default:
                Utility.CodingError("Unexpected record type {0}", logRecord.RecordType);
                break;
            }
        }
예제 #13
0
        /// <summary>
        /// Processes the log record.
        /// </summary>
        /// <param name="logRecord">The log record to be processed.</param>
        public void ProcessLogRecord(LogRecord logRecord)
        {
            var isRecoverableRecord = true;

            this.ProcessLogRecord(logRecord, out isRecoverableRecord);
        }
예제 #14
0
 internal async Task <LogRecord> GetPreviousLogRecord(ulong recordPosition)
 {
     this.readStream.Position = (long)recordPosition;
     return(await LogRecord.ReadPreviousRecordAsync(this.readStream).ConfigureAwait(false));
 }
예제 #15
0
        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);
        }
예제 #16
0
        public async Task ImmediatelyProcessRecord(
            LogRecord record,
            Exception flushException,
            RecordProcessingMode processingMode)
        {
            Utility.Assert(
                processingMode > RecordProcessingMode.Normal,
                "processingMode ({0}) > RecordProcessingMode.Normal",
                processingMode);

            // TODO: Temporary double check.
            Utility.Assert(record.RecordType != LogRecordType.Backup, "record.RecordType != LogRecordType.Backup");

            Exception exception = null;

            if (flushException != null)
            {
                Utility.Assert(
                    this.logException != null,
                    "FlushException is {0} and this.logException is null",
                    flushException);
                exception = flushException;
            }

            // In case there was an apply failure, we should fault any further applies of any records.
            // Without this, we could end up assuming that all applies have succeeded and as a result, issue a checkpoint call
            if (exception == null)
            {
                exception = this.serviceException;
            }

            FabricEvents.Events.RecordProcessedImmediatelyNoise(
                this.tracer.Type,
                (int)this.roleContextDrainState.DrainingStream,
                record.Psn.PSN);

            var information = string.Empty;

            switch (record.RecordType)
            {
            case LogRecordType.TruncateHead:
                this.checkpointManager.ApplyLogHeadTruncationIfPermitted(exception, record);
                break;

            case LogRecordType.BeginCheckpoint:
                if (processingMode == RecordProcessingMode.ProcessImmediately)
                {
                    goto default;
                }

                await this.checkpointManager.ApplyCheckpointIfPermitted(exception, record).ConfigureAwait(false);

                break;

            case LogRecordType.Information:
                information = '-' + ((InformationLogRecord)record).InformationEvent.ToString();
                goto case LogRecordType.EndCheckpoint;

            case LogRecordType.EndCheckpoint:
            case LogRecordType.TruncateTail:
            case LogRecordType.Indexing:
            case LogRecordType.UpdateEpoch:
                goto default;

            default:
                record.CompletedApply(exception);
                break;
            }

            if (processingMode == RecordProcessingMode.ProcessImmediately)
            {
                FabricEvents.Events.RecordProcessedImmediately(
                    this.tracer.Type,
                    (int)this.roleContextDrainState.DrainingStream,
                    information,
                    record.Psn.PSN);

                record.CompletedProcessing(null);
            }

            return;
        }
예제 #17
0
        /// <summary>
        /// Write the backup log file.
        /// </summary>
        /// <param name="stream">Backup log stream.</param>
        /// <param name="logRecords">The log records.</param>
        /// <param name="cancellationToken">Token used to signal cancellation.</param>
        /// <returns>Task that represents the asynchronous operation.</returns>
        private async Task WriteLogRecordsAsync(
            Stream stream,
            IAsyncEnumerator <LogRecord> logRecords,
            CancellationToken cancellationToken)
        {
            var firstIndexingRecordProcessed = false;

            using (var memoryStream = new MemoryStream(InitialSizeOfMemoryStream))
                using (var binaryWriter = new BinaryWriter(memoryStream))
                    using (var recordWriter = new BinaryWriter(new MemoryStream()))
                    {
                        // Reserve an 'int' at the start to store the size of the block
                        memoryStream.Position += sizeof(int);

                        while (true == await logRecords.MoveNextAsync(cancellationToken).ConfigureAwait(false))
                        {
                            var record = logRecords.Current;

                            var indexingLogRecord = record as IndexingLogRecord;
                            if (indexingLogRecord != null)
                            {
                                if (false == firstIndexingRecordProcessed)
                                {
                                    this.IndexingRecordEpoch = indexingLogRecord.CurrentEpoch;
                                    this.IndexingRecordLsn   = indexingLogRecord.Lsn;

                                    firstIndexingRecordProcessed = true;
                                }

                                this.LastBackedUpEpoch = indexingLogRecord.CurrentEpoch;
                            }

                            var updateEpochLogRecord = record as UpdateEpochLogRecord;
                            if (updateEpochLogRecord != null)
                            {
                                this.LastBackedUpEpoch = updateEpochLogRecord.Epoch;
                            }

                            this.LastBackedUpLsn = record.Lsn;

                            var operationData = LogRecord.WriteRecord(record, recordWriter, isPhysicalWrite: false, setRecordLength: false);

                            // TODO: Fix this by using operationdata in this class
                            foreach (var buffer in operationData)
                            {
                                binaryWriter.Write(buffer.Array, buffer.Offset, buffer.Count);
                            }

                            this.Count++;

                            // Flush the block after it's large enough.
                            if (memoryStream.Position >= MinimumIntermediateFlushSize)
                            {
                                await this.WriteLogRecordBlockAsync(stream, memoryStream, binaryWriter, cancellationToken).ConfigureAwait(false);
                            }

                            // Check for cancellation.
                            cancellationToken.ThrowIfCancellationRequested();
                        }

                        // Flush the block, if there's any remaining data (ignoring the block size 'int' at the start).
                        if (memoryStream.Position > sizeof(int))
                        {
                            await this.WriteLogRecordBlockAsync(stream, memoryStream, binaryWriter, cancellationToken).ConfigureAwait(false);
                        }

                        await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
                    }

            // If no logical record is being backed up, lastBackedupLsn can be Invalid.
            if (this.LastBackedUpLsn == LogicalSequenceNumber.InvalidLsn)
            {
                this.LastBackedUpLsn = this.IndexingRecordLsn;
            }

            Utility.Assert(true == firstIndexingRecordProcessed, "Indexing log record must have been processed.");

            Utility.Assert(
                this.IndexingRecordEpoch.CompareTo(LoggingReplicator.InvalidEpoch) > 0,
                "Indexing record epoch has not been set.");
            Utility.Assert(
                this.LastBackedUpEpoch.CompareTo(LoggingReplicator.InvalidEpoch) > 0,
                "Ending epoch has not been set.");

            Utility.Assert(
                this.IndexingRecordLsn != LogicalSequenceNumber.InvalidLsn,
                "Indexing record lsn has not been set.");
            Utility.Assert(this.LastBackedUpLsn != LogicalSequenceNumber.InvalidLsn, "Ending lsn has not been set.");
        }
예제 #18
0
        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));
        }