Example #1
0
        internal EndTransactionLogRecord FindUnstableTransaction(EndTransactionLogRecord record)
        {
            Utility.Assert(
                record.Transaction.IsAtomicOperation == false,
                "record.Transaction.IsAtomicOperation == false");
            Utility.Assert(
                LogRecord.IsInvalidRecord(record.ParentTransactionRecord) == true,
                "LogRecord.IsInvalidRecord(record.ParentTransactionRecord) == true");

            int i;

            lock (this.txmapLock)
            {
                for (i = this.unstableTransactions.Count - 1; i >= 0; i--)
                {
                    if (this.unstableTransactions[i].Transaction == record.Transaction)
                    {
                        record = this.unstableTransactions[i];
                        break;
                    }
                }
            }

            Utility.Assert(
                i >= 0,
                "Transaction associated with the end transaction log record is not presnet in unstable transactions");
            return(record);
        }
Example #2
0
        internal OperationLogRecord RedactOperation(OperationLogRecord record)
        {
            Utility.Assert(record.Transaction.IsValidTransaction, "record.Transaction.IsValidTransaction == true");
            Utility.Assert(record.IsOperationContextPresent == false, "record.IsOperationContextPresent == false");

            if (record.Transaction.IsAtomicOperation)
            {
                Utility.Assert(record.ParentTransactionRecord == null, "record.ParentTransactionRecord == null");
            }
            else
            {
                Utility.Assert(
                    LogRecord.IsInvalidRecord(record.ParentTransactionRecord),
                    "LogRecord.IsInvalidRecord(record.ParentTransactionRecord) == true");
                lock (this.txmapLock)
                {
                    Utility.Assert(
                        this.latestRecords.ContainsKey(record.Transaction.Id),
                        "latestRecords_.ContainsKey(record.Transaction.Id)");

                    record = (OperationLogRecord)this.latestRecords[record.Transaction.Id];
                    this.latestRecords[record.Transaction.Id]             = record.ParentTransactionRecord;
                    record.ParentTransactionRecord.ChildTransactionRecord =
                        TransactionLogRecord.InvalidTransactionLogRecord;
                    return(record);
                }
            }

            return(record);
        }
Example #3
0
        internal OperationLogRecord FindOperation(OperationLogRecord record)
        {
            Utility.Assert(record.Transaction.IsValidTransaction, "record.Transaction.IsValidTransaction == true");
            Utility.Assert(record.IsOperationContextPresent == false, "record.IsOperationContextPresent == false");

            if (record.Transaction.IsAtomicOperation)
            {
                Utility.Assert(record.ParentTransactionRecord == null, "record.ParentTransactionRecord == null");
            }
            else
            {
                Utility.Assert(
                    LogRecord.IsInvalidRecord(record.ParentTransactionRecord),
                    "LogRecord.IsInvalidRecord(record.ParentTransactionRecord) == true");
                lock (this.txmapLock)
                {
                    Utility.Assert(
                        this.latestRecords.ContainsKey(record.Transaction.Id),
                        "Could not find operation in latest records");
                    return((OperationLogRecord)this.latestRecords[record.Transaction.Id]);
                }
            }

            return(record);
        }
        /// <summary>
        /// Inserts a record to the buffer where it waits to be flushed in batches.
        /// A successful run will set following properties in the record.
        ///     record.Psn and record.PreviousPhysicalLogRecord
        /// </summary>
        /// <param name="record"></param>
        /// <returns>The pending size of the buffered records</returns>
        internal long InsertBufferedRecord(LogRecord record)
        {
            Utility.Assert(LogRecord.IsInvalidRecord(record) == false, "LogRecord.IsInvalidRecord(record) == false");

            var physicalRecord = record as PhysicalLogRecord;

            lock (this.flushLock)
            {
                if (this.closedException == null)
                {
                    // No record can be inserted after 'RemovingState' Information log record
                    if (this.lastPhysicalRecord != null &&
                        this.lastPhysicalRecord.RecordType == LogRecordType.Information)
                    {
                        var lastInformationRecord = this.lastPhysicalRecord as InformationLogRecord;
                        Utility.Assert(
                            lastInformationRecord.InformationEvent != InformationEvent.RemovingState,
                            "No record should be inserted after 'RemovingState'. Current Record = {0}",
                            record);
                    }

                    // Update record and tail
                    ++this.currentLogTailPsn;
                    record.Psn = this.currentLogTailPsn;
                    this.incomingBytesRateCounterWriter.IncrementBy(record.ApproximateSizeOnDisk);

                    record.PreviousPhysicalRecord = this.lastPhysicalRecord;
                    if (physicalRecord != null)
                    {
                        if (this.lastPhysicalRecord != null)
                        {
                            this.lastPhysicalRecord.NextPhysicalRecord = physicalRecord;
                        }

                        this.lastPhysicalRecord = physicalRecord;
                    }

                    // Buffer record
                    if (this.bufferedRecords == null)
                    {
                        this.bufferedRecords = new List <LogRecord>();
                    }

                    this.bufferedRecords.Add(record);
                    var newBufferedRecordsBytes = Interlocked.Add(
                        ref this.bufferedRecordsBytes,
                        record.ApproximateSizeOnDisk);

                    return(newBufferedRecordsBytes);
                }
            }

            var failedRecord = new LoggedRecords(record, this.closedException);

            this.ProcessFlushedRecords(failedRecord);

            return(Interlocked.Read(ref this.bufferedRecordsBytes));
        }
Example #5
0
        internal async Task IndexPhysicalRecords(PhysicalLogRecord record)
        {
            while (record.RecordPosition > this.startingRecordPosition)
            {
                record = await this.GetPreviousPhysicalRecord(record).ConfigureAwait(false);
            }

            Utility.Assert(
                LogRecord.IsInvalidRecord(this.startingRecord) == false,
                "LogRecord.IsInvalidRecord(this.startingRecord) == false");
        }
Example #6
0
        internal async Task <LogRecord> SeekToLastRecord()
        {
            Utility.Assert(this.endingRecordPosition == long.MaxValue, "this.endingRecordPosition == Int64.MaxValue");
            Utility.Assert(
                LogRecord.IsInvalidRecord(this.endRecord),
                "LogRecord.IsInvalidRecord(this.endRecord) == true. this.endRecord: {0}",
                this.endRecord);

            this.readStream.Position = this.readStream.Length;
            this.endRecord           = await LogRecord.ReadPreviousRecordAsync(this.readStream).ConfigureAwait(false);

            return(this.endRecord);
        }
Example #7
0
        internal async Task <PhysicalLogRecord> GetPreviousPhysicalRecord(LogRecord record)
        {
            var previousPhysicalRecord = record.PreviousPhysicalRecord;

            if (LogRecord.IsInvalidRecord(previousPhysicalRecord))
            {
                var previousPhysicalRecordOffset = record.PreviousPhysicalRecordOffset;
                Utility.Assert(
                    previousPhysicalRecordOffset != LogRecord.InvalidPhysicalRecordOffset,
                    "previousPhysicalRecordOffset != LogRecord.INVALID_PHYSICAL_RECORD_OFFSET");
                if (previousPhysicalRecordOffset == 0)
                {
                    record.PreviousPhysicalRecord = null;
                    return(null);
                }

                // Read previous physical record
                var recordPosition = record.RecordPosition;
                Utility.Assert(
                    recordPosition != LogRecord.InvalidRecordPosition,
                    "recordPosition != LogRecord.INVALID_RECORD_POSITION");
                var previousPhysicalRecordPosition = recordPosition - previousPhysicalRecordOffset;
                Utility.Assert(
                    previousPhysicalRecordPosition >= this.startingRecordPosition,
                    "previousPhysicalRecordPosition >= this.startingRecordPosition");

                previousPhysicalRecord = (PhysicalLogRecord)await this.GetNextLogRecord(previousPhysicalRecordPosition).ConfigureAwait(false);

                var nextPhysicalLogRecord = record as PhysicalLogRecord;
                if (nextPhysicalLogRecord != null)
                {
                    previousPhysicalRecord.NextPhysicalRecord = nextPhysicalLogRecord;
                }

                record.PreviousPhysicalRecord = previousPhysicalRecord;
                if (previousPhysicalRecordPosition == this.startingRecordPosition)
                {
                    this.startingRecord = previousPhysicalRecord;
                }
            }

            Utility.Assert(
                (previousPhysicalRecord == null) == (record.PreviousPhysicalRecordOffset == 0),
                "(previousPhysicalRecord == null) == (record.PreviousPhysicalRecordOffset == 0)");
            return(previousPhysicalRecord);
        }
Example #8
0
        internal async Task <BeginCheckpointLogRecord> GetLastCompletedBeginCheckpointRecord(
            EndCheckpointLogRecord record)
        {
            var lastCompletedBeginCheckpointRecord = record.LastCompletedBeginCheckpointRecord;

            if (!LogRecord.IsInvalidRecord(lastCompletedBeginCheckpointRecord))
            {
                return(lastCompletedBeginCheckpointRecord);
            }

            var lastCompletedBeginCheckpointRecordOffset = record.LastCompletedBeginCheckpointRecordOffset;

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

            // Read desired checkpoint record
            var recordPosition = record.RecordPosition;

            Utility.Assert(
                recordPosition != LogRecord.InvalidRecordPosition,
                "recordPosition ({0}) != LogRecord.INVALID_RECORD_POSITION",
                recordPosition);

            var lastCompletedBeginCheckpointRecordPosition = recordPosition - lastCompletedBeginCheckpointRecordOffset;

            Utility.Assert(
                lastCompletedBeginCheckpointRecordPosition >= this.startingRecordPosition,
                "lastCompletedBeginCheckpointRecordPosition ({0}) >= this.startingRecordPosition ({1})",
                lastCompletedBeginCheckpointRecordPosition, this.startingRecordPosition);

            lastCompletedBeginCheckpointRecord =
                (BeginCheckpointLogRecord)await this.GetNextLogRecord(lastCompletedBeginCheckpointRecordPosition).ConfigureAwait(false);

            record.LastCompletedBeginCheckpointRecord = lastCompletedBeginCheckpointRecord;

            return(lastCompletedBeginCheckpointRecord);
        }
Example #9
0
        internal async Task <PhysicalLogRecord> GetLinkedPhysicalRecord(PhysicalLogRecord record)
        {
            var linkedPhysicalRecord = record.LinkedPhysicalRecord;

            if (LogRecord.IsInvalidRecord(linkedPhysicalRecord))
            {
                var linkedPhysicalRecordOffset = record.LinkedPhysicalRecordOffset;
                Utility.Assert(
                    linkedPhysicalRecordOffset != LogRecord.InvalidPhysicalRecordOffset,
                    "linkedPhysicalRecordOffset != PhysicalLogRecord.INVALID_PHYSICAL_RECORD_OFFSET");
                if (linkedPhysicalRecordOffset == 0)
                {
                    record.LinkedPhysicalRecord = null;
                    return(null);
                }

                // Read desired linked record
                var recordPosition = record.RecordPosition;
                Utility.Assert(
                    recordPosition != LogRecord.InvalidRecordPosition,
                    "recordPosition != LogRecord.INVALID_RECORD_POSITION");
                var linkedPhysicalRecordPosition = recordPosition - linkedPhysicalRecordOffset;
                Utility.Assert(
                    linkedPhysicalRecordPosition >= this.startingRecordPosition,
                    "linkedPhysicalRecordPosition >= this.startingRecordPosition");

                linkedPhysicalRecord = (PhysicalLogRecord)await this.GetNextLogRecord(linkedPhysicalRecordPosition).ConfigureAwait(false);

                record.LinkedPhysicalRecord = linkedPhysicalRecord;
            }

            Utility.Assert(
                (linkedPhysicalRecord == null) == (record.LinkedPhysicalRecordOffset == 0),
                "(linkedPhysicalRecord == null) == (record.LinkedPhysicalRecordOffset == 0)");
            return(linkedPhysicalRecord);
        }
Example #10
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);
        }
Example #11
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);
        }
Example #12
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;
        }
Example #13
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;
        }
Example #14
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));
        }