示例#1
0
        /// <summary>
        /// Primary: Process the logical log record.
        /// </summary>
        /// <param name="record"></param>
        /// <returns></returns>
        /// <remarks>
        /// Wait for the logical record to be flushed and replicated.
        /// If it is a barrier, increase stable lsn.
        /// If it is a operation record, unlock it.
        /// </remarks>
        private async Task ProcessLogicalRecordOnPrimaryAsync(TransactionLogRecord record)
        {
            Exception exception = null;

            this.ReplicateAndLogLogicalRecord(record);

            try
            {
                // Wait for the record to be flushed and replicated
                await Task.WhenAll(record.AwaitApply(), LoggingReplicator.AwaitReplication(record, this.replicatedLogManager)).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                exception = e;
            }
            finally
            {
                this.InvokeUnlockCallback(record);
            }

            // Throw TransactionFaultedException if there has been any failure during replication and apply awaits.
            if (exception != null)
            {
                throw new TransactionFaultedException(exception.Message);
            }

            return;
        }
        private void ReadPrivate(BinaryReader br, bool isPhysicalRead)
        {
            var logicalStartingPosition = br.BaseStream.Position;
            var logicalSizeOfSection    = br.ReadInt32();
            var logicalEndPosition      = logicalStartingPosition + logicalSizeOfSection;

            this.transaction = TransactionBase.CreateTransaction(br.ReadInt64(), true);

            // Jump to the end of the section ignoring fields that are not understood.
            Utility.Assert(logicalEndPosition >= br.BaseStream.Position, "Could not have read more than section size.");
            br.BaseStream.Position = logicalEndPosition;

            if (isPhysicalRead)
            {
                // Metadata section.
                var physicalStartPosition = br.BaseStream.Position;
                var physicalSectionSize   = br.ReadInt32();
                var physicalEndPosition   = physicalStartPosition + physicalSectionSize;

                this.parentTransactionRecordOffset = br.ReadUInt64();
                if (this.parentTransactionRecordOffset == 0)
                {
                    this.parentTransactionRecord = null;
                }

                // Jump to the end of the section ignoring fields that are not understood.
                Utility.Assert(physicalEndPosition >= br.BaseStream.Position, "Could not have read more than section size.");
                br.BaseStream.Position = physicalEndPosition;
            }

            this.UpdateApproximateDiskSize();
        }
示例#3
0
        private long Append(
            LogicalLogRecord record,
            TransactionManager transactionsManager,
            bool isPrimary)
        {
            long bufferedBytes = 0;
            TransactionLogRecord          txLogRecord            = record as TransactionLogRecord;
            TaskCompletionSource <object> lsnOrderingTcsToSignal = null;

            if (txLogRecord != null)
            {
                Utility.Assert(transactionsManager != null, "transactionsManager should not be null in AppendOnPrimary");
                transactionsManager.CreateTransactionState(this.CurrentLogTailEpoch, txLogRecord);
            }

            lock (this.lsnOrderingLock)
            {
                bufferedBytes = LsnOrderingInsert(record, isPrimary, out lsnOrderingTcsToSignal);
            }

            if (lsnOrderingTcsToSignal != null)
            {
                // Do this outside the lock above and also after setting the this.lsnorderingtcs to NULL as this set result can end up invoking a completion to run on the same thread,
                // which ends up invoking another call to Append. This happened in a unit test on the UpdateEpoch code path on primary
                bool result = lsnOrderingTcsToSignal.TrySetResult(null);
                Utility.Assert(result == true, "ReplicatedLogManager: failed to set result on lsn ordering tcs");
            }

            return(bufferedBytes);
        }
        protected TransactionLogRecord()
            : base()
        {
            this.transaction = TransactionBase.InvalidTransaction;
            this.parentTransactionRecordOffset = InvalidLogicalRecordOffset;

            this.parentTransactionRecord = InvalidTransactionLogRecord;
            this.childTransactionRecord  = new WeakReference <TransactionLogRecord>(InvalidTransactionLogRecord);
            this.isEnlistedTransaction   = false;
        }
        internal TransactionLogRecord(LogRecordType recordType, ulong recordPosition, long lsn)
            : base(recordType, recordPosition, lsn)
        {
            this.transaction = TransactionBase.InvalidTransaction;
            this.parentTransactionRecordOffset = InvalidLogicalRecordOffset;

            this.parentTransactionRecord = InvalidTransactionLogRecord;
            this.childTransactionRecord  = new WeakReference <TransactionLogRecord>(InvalidTransactionLogRecord);
            this.isEnlistedTransaction   = false;
        }
        internal TransactionLogRecord(
            LogRecordType recordType,
            TransactionBase transaction,
            TransactionLogRecord parentLogicalRecord)
            : base(recordType)
        {
            this.transaction = transaction;
            this.parentTransactionRecordOffset = InvalidLogicalRecordOffset;

            this.parentTransactionRecord = parentLogicalRecord;
            this.childTransactionRecord  = new WeakReference <TransactionLogRecord>(InvalidTransactionLogRecord);
            this.isEnlistedTransaction   = false;

            this.UpdateApproximateDiskSize();
        }
示例#7
0
        private void ProcessLogicalRecordOnPrimary(TransactionLogRecord record)
        {
            this.ReplicateAndLogLogicalRecord(record);

            Task.WhenAll(record.AwaitApply(), LoggingReplicator.AwaitReplication(record, this.replicatedLogManager)).IgnoreException().ContinueWith(
                task =>
            {
                // Simply retrieve task exception to mark it as handled
                if (task.Exception != null)
                {
                    Utility.Assert(task.IsFaulted == true, "task.IsFaulted == true");
                }

                this.InvokeUnlockCallback(record);
            }).IgnoreExceptionVoid();
        }
示例#8
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);
        }
示例#9
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);
        }
示例#10
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;
        }
示例#11
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;
        }
示例#12
0
        public void CreateTransactionState(
            Epoch logTailEpoch,
            TransactionLogRecord record)
        {
            switch (record.RecordType)
            {
            case LogRecordType.BeginTransaction:
                var beginTxRecord = record as BeginTransactionOperationLogRecord;

                this.beginTransactionOperationRatePerformanceCounterWriter.Increment();

                if (beginTxRecord.IsSingleOperationTransaction)
                {
                    this.transactionCommitRatePerformanceCounterWriter.Increment();

                    FabricEvents.Events.AcceptSingleOperationTransaction(
                        tracer.Type,
                        beginTxRecord.Transaction.Id,
                        record.Lsn.LSN);

                    record.Transaction.CommitSequenceNumber = record.Lsn.LSN;
                }
                else
                {
                    FabricEvents.Events.AcceptBeginTransaction(
                        tracer.Type,
                        beginTxRecord.Transaction.Id,
                        record.Lsn.LSN);
                }

                beginTxRecord.RecordEpoch = logTailEpoch;
                this.transactionMap.CreateTransaction(beginTxRecord);
                break;

            case LogRecordType.Operation:
                var dataRecord = record as OperationLogRecord;

                if (dataRecord.Transaction.IsAtomicOperation || dataRecord.IsRedoOnly)
                {
                    this.addAtomicOperationRatePerformanceCounterWriter.Increment();
                    dataRecord.Transaction.CommitSequenceNumber = record.Lsn.LSN;
                }
                else
                {
                    this.addOperationRatePerformanceCounterWriter.Increment();
                }

                FabricEvents.Events.AcceptOperation(tracer.Type, dataRecord.Transaction.Id, record.Lsn.LSN);

                this.transactionMap.AddOperation(dataRecord);
                Utility.Assert(
                    (dataRecord.IsEnlistedTransaction == true) ||
                    (dataRecord.Lsn <= this.recoveredOrCopiedCheckpointLsn.Value),
                    "(record.IsEnlistedTransaction == true) || (record.LastLogicalSequenceNumber <= this.recoveredOrCopiedCheckpointLsn). record LSN :{0} and recoverd or copied checkpoint lsn:{1}. record.InEnlistedTx :{2}",
                    dataRecord.Lsn, recoveredOrCopiedCheckpointLsn,
                    dataRecord.IsEnlistedTransaction);
                break;

            case LogRecordType.EndTransaction:
                var endTxRecord = record as EndTransactionLogRecord;

                if (endTxRecord.IsCommitted)
                {
                    this.transactionCommitRatePerformanceCounterWriter.Increment();
                    endTxRecord.Transaction.CommitSequenceNumber = record.Lsn.LSN;
                }
                else
                {
                    this.transactionAbortRatePerformanceCounterWriter.Increment();
                }

                FabricEvents.Events.AcceptEndTransaction(
                    tracer.Type,
                    endTxRecord.Transaction.Id,
                    record.Lsn.LSN,
                    endTxRecord.IsCommitted);

                this.transactionMap.CompleteTransaction(endTxRecord);
                Utility.Assert(
                    (endTxRecord.IsEnlistedTransaction == true) ||
                    (record.Lsn <= this.recoveredOrCopiedCheckpointLsn.Value),
                    "(record.IsEnlistedTransaction == true) || (record.LastLogicalSequenceNumber <= this.recoveredOrCopiedCheckpointLsn). record LSN :{0} and recoverd or copied checkpoint lsn:{1}. record.InEnlistedTx :{2}",
                    record.Lsn,
                    this.recoveredOrCopiedCheckpointLsn.Value.LSN,
                    endTxRecord.IsEnlistedTransaction);
                break;

            default:
                Utility.CodingError("Unexpected record type in TransactionsManager CreateTransactionState {0}", record.RecordType);
                break;
            }
        }
示例#13
0
 private void InvokeUnlockCallback(TransactionLogRecord record)
 {
     this.unlockCallbackManager.Unlock(record);
 }
示例#14
0
 private void ReplicateAndLogLogicalRecord(TransactionLogRecord record)
 {
     this.InitiateLogicalRecordProcessingOnPrimary(record);
     return;
 }