Exemple #1
0
        public static OrleansTransactionException ConvertToUserException(this TransactionalStatus status, string transactionId, Exception exception)
        {
            switch (status)
            {
            case TransactionalStatus.PrepareTimeout:
                return(new OrleansTransactionPrepareTimeoutException(transactionId, exception));

            case TransactionalStatus.CascadingAbort:
                return(new OrleansCascadingAbortException(transactionId, exception));

            case TransactionalStatus.BrokenLock:
                return(new OrleansBrokenTransactionLockException(transactionId, "before prepare", exception));

            case TransactionalStatus.LockValidationFailed:
                return(new OrleansBrokenTransactionLockException(transactionId, "when validating accesses during prepare", exception));

            case TransactionalStatus.ParticipantResponseTimeout:
                return(new OrleansTransactionTransientFailureException(transactionId, $"transaction agent timed out waiting for read-only transaction participant responses ({status})", exception));

            case TransactionalStatus.TMResponseTimeout:
                return(new OrleansTransactionInDoubtException(transactionId, $"transaction agent timed out waiting for read-only transaction participant responses ({status})", exception));

            case TransactionalStatus.CommitFailure:
                return(new OrleansTransactionAbortedException(transactionId, $"Unable to commit transaction ({status})", exception));

            default:
                return(new OrleansTransactionInDoubtException(transactionId, $"failure during transaction commit, status={status}", exception));
            }
        }
Exemple #2
0
        private async Task <TransactionalStatus> CommitReadWriteTransaction(TransactionInfo transactionInfo, List <ParticipantId> writeResources)
        {
            ParticipantId manager = SelectManager(transactionInfo, writeResources);
            Dictionary <ParticipantId, AccessCounter> participants = transactionInfo.Participants;

            foreach (var p in participants
                     .SelectResources()
                     .Where(kvp => !kvp.Key.Equals(manager)))
            {
                // one-way prepare message
                p.Key.Reference.AsReference <ITransactionalResourceExtension>()
                .Prepare(p.Key.Name, transactionInfo.TransactionId, p.Value, transactionInfo.TimeStamp, manager)
                .Ignore();
            }

            try
            {
                // wait for the TM to commit the transaction
                TransactionalStatus status = await manager.Reference.AsReference <ITransactionManagerExtension>()
                                             .PrepareAndCommit(manager.Name, transactionInfo.TransactionId, participants[manager], transactionInfo.TimeStamp, writeResources, participants.Count);

                if (status != TransactionalStatus.Ok)
                {
                    if (logger.IsEnabled(LogLevel.Debug))
                    {
                        logger.Debug($"{stopwatch.Elapsed.TotalMilliseconds:f2} fail {transactionInfo.TransactionId} TM response status={status}");
                    }

                    // notify participants
                    if (status.DefinitelyAborted())
                    {
                        await Task.WhenAll(writeResources
                                           .Where(p => !p.Equals(manager))
                                           .Select(p => p.Reference.AsReference <ITransactionalResourceExtension>()
                                                   .Cancel(p.Name, transactionInfo.TransactionId, transactionInfo.TimeStamp, status)));
                    }

                    return(status);
                }
            }
            catch (TimeoutException)
            {
                if (logger.IsEnabled(LogLevel.Debug))
                {
                    logger.Debug($"{stopwatch.Elapsed.TotalMilliseconds:f2} timeout {transactionInfo.TransactionId} TM response");
                }

                return(TransactionalStatus.TMResponseTimeout);
            }

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace($"{stopwatch.Elapsed.TotalMilliseconds:f2} finish {transactionInfo.TransactionId}");
            }

            return(TransactionalStatus.Ok);
        }
Exemple #3
0
        private void AbortCommits(TransactionalStatus status, int from = 0)
        {
            // emtpy the back of the commit queue, starting at specified position
            for (int i = from; i < commitQueue.Count; i++)
            {
                NotifyOfAbort(commitQueue[i], i == from ? status : TransactionalStatus.CascadingAbort);
            }
            commitQueue.RemoveFromBack(commitQueue.Count - from);

            this.RWLock.AbortExecutingTransactions();
        }
        public void NotifyOfPrepared(Guid transactionId, DateTime timeStamp, TransactionalStatus status)
        {
            var pos = commitQueue.Find(transactionId, timeStamp);

            if (pos != -1)
            {
                var localEntry = commitQueue[pos];

                if (localEntry.Role != CommitRole.LocalCommit)
                {
                    logger.Error(666, $"transaction abort due to internal error in {nameof(NotifyOfPrepared)}: Wrong commit type");
                    throw new InvalidOperationException($"Wrong commit type: {localEntry.Role}");
                }

                if (status == TransactionalStatus.Ok)
                {
                    localEntry.WaitCount--;

                    storageWorker.Notify();
                }
                else
                {
                    AbortCommits(status, pos);

                    this.RWLock.Notify();
                }
            }
            else
            {
                // this message has arrived ahead of the commit request - we need to remember it

                if (this.unprocessedPreparedMessages == null)
                {
                    this.unprocessedPreparedMessages = new Dictionary <DateTime, PMessages>();
                }

                PMessages info;
                if (!this.unprocessedPreparedMessages.TryGetValue(timeStamp, out info))
                {
                    this.unprocessedPreparedMessages[timeStamp] = info = new PMessages();
                }
                if (status == TransactionalStatus.Ok)
                {
                    info.Count++;
                }
                else
                {
                    info.Status = status;
                }

                // TODO fix memory leak if corresponding commit messages never arrive
            }
        }
        public async Task Cancel(Guid transactionId, DateTime timeStamp, TransactionalStatus status)
        {
            this.logger.Info($"Grain {this.context.GrainInstance} canceling transaction {transactionId}");
            await this.tResource.Cancel(transactionId, timeStamp, status);

            if (this.deactivationPhaseReference.DeactivationPhase == TransactionDeactivationPhase.AfterCancel)
            {
                this.grainRuntime.DeactivateOnIdle((context.GrainInstance));
                this.deactivationPhaseReference.DeactivationPhase = TransactionDeactivationPhase.None;
                this.logger.Info($"Grain {this.context.GrainInstance} deactivating after transaction {transactionId} cancel");
            }
        }
Exemple #6
0
        public async Task Cancel(Guid transactionId, DateTime timeStamp, TransactionalStatus status)
        {
            this.logger.Info($"Grain {this.context.GrainInstance} canceling transaction {transactionId}");
            await this.tResource.Cancel(transactionId, timeStamp, status);

            if (this.faultInjectionControl.FaultInjectionPhase == TransactionFaultInjectPhase.AfterCancel &&
                this.faultInjectionControl.FaultInjectionType == FaultInjectionType.Deactivation)
            {
                this.grainRuntime.DeactivateOnIdle((context.GrainInstance));
                this.logger.Info($"Grain {this.context.GrainInstance} deactivating after transaction {transactionId} cancel");
            }
            this.faultInjectionControl.Reset();
        }
Exemple #7
0
        public async Task NotifyOfPrepared(Guid transactionId, DateTime timeStamp, TransactionalStatus status)
        {
            var pos = commitQueue.Find(transactionId, timeStamp);

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.LogTrace("NotifyOfPrepared - TransactionId:{TransactionId} Timestamp:{Timestamp}, TransactionalStatus{TransactionalStatus}", transactionId, timeStamp, status);
            }

            if (pos != -1)
            {
                var localEntry = commitQueue[pos];

                if (localEntry.Role != CommitRole.LocalCommit)
                {
                    logger.LogError($"Transaction abort due to internal error in {nameof(NotifyOfPrepared)}: Wrong commit type");
                    throw new InvalidOperationException($"Wrong commit type: {localEntry.Role}");
                }

                if (status == TransactionalStatus.Ok)
                {
                    localEntry.WaitCount--;

                    storageWorker.Notify();
                }
                else
                {
                    await AbortCommits(status, pos);

                    this.RWLock.Notify();
                }
            }
            else
            {
                // this message has arrived ahead of the commit request - we need to remember it
                if (!this.unprocessedPreparedMessages.TryGetValue(timeStamp, out PreparedMessages info))
                {
                    this.unprocessedPreparedMessages[timeStamp] = info = new PreparedMessages(status);
                }
                if (status == TransactionalStatus.Ok)
                {
                    info.Count++;
                }
                else
                {
                    info.Status = status;
                }

                // TODO fix memory leak if corresponding commit messages never arrive
            }
        }
Exemple #8
0
        private async Task AbortCommits(TransactionalStatus status, int from = 0)
        {
            List <Task> pending = new List <Task>();

            // emtpy the back of the commit queue, starting at specified position
            for (int i = from; i < commitQueue.Count; i++)
            {
                pending.Add(NotifyOfAbort(commitQueue[i], i == from ? status : TransactionalStatus.CascadingAbort));
            }
            commitQueue.RemoveFromBack(commitQueue.Count - from);

            pending.Add(this.RWLock.AbortExecutingTransactions());
            await Task.WhenAll(pending);
        }
Exemple #9
0
        public static bool DefinitelyAborted(this TransactionalStatus status)
        {
            switch (status)
            {
            case TransactionalStatus.PrepareTimeout:
            case TransactionalStatus.CascadingAbort:
            case TransactionalStatus.BrokenLock:
            case TransactionalStatus.LockValidationFailed:
            case TransactionalStatus.ParticipantResponseTimeout:
                return(true);

            default:
                return(false);
            }
        }
Exemple #10
0
        public async Task <TransactionalStatus> Commit(ITransactionInfo info)
        {
            var transactionInfo = (TransactionInfo)info;

            transactionInfo.TimeStamp = this.clock.MergeUtcNow(transactionInfo.TimeStamp);

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace($"{stopwatch.Elapsed.TotalMilliseconds:f2} prepare {transactionInfo}");
            }

            List <ParticipantId> writeParticipants = null;

            foreach (var p in transactionInfo.Participants)
            {
                if (p.Value.Writes > 0)
                {
                    if (writeParticipants == null)
                    {
                        writeParticipants = new List <ParticipantId>();
                    }
                    writeParticipants.Add(p.Key);
                }
            }

            try
            {
                TransactionalStatus status = (writeParticipants == null)
                    ? await CommitReadOnlyTransaction(transactionInfo)
                    : await CommitReadWriteTransaction(transactionInfo, writeParticipants);

                if (status == TransactionalStatus.Ok)
                {
                    this.statistics.TrackTransactionSucceeded();
                }
                else
                {
                    this.statistics.TrackTransactionFailed();
                }
                return(status);
            }
            catch (Exception)
            {
                this.statistics.TrackTransactionFailed();
                throw;
            }
        }
        public void NotifyOfCancel(Guid transactionId, DateTime timeStamp, TransactionalStatus status)
        {
            // find in queue
            var pos = commitQueue.Find(transactionId, timeStamp);

            if (pos == -1)
            {
                return;
            }

            this.storageBatch.Cancel(commitQueue[pos].SequenceNumber);

            AbortCommits(status, pos);

            storageWorker.Notify();

            this.RWLock.Notify();
        }
        public async Task <TransactionalStatus> Resolve(ITransactionInfo info)
        {
            var transactionInfo = (TransactionInfo)info;

            transactionInfo.TimeStamp = this.clock.MergeUtcNow(transactionInfo.TimeStamp);

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace($"{stopwatch.Elapsed.TotalMilliseconds:f2} prepare {transactionInfo}");
            }

            if (transactionInfo.Participants.Count == 0)
            {
                this.statistics.TrackTransactionSucceeded();
                return(TransactionalStatus.Ok);
            }

            List <ParticipantId> writeParticipants = null;
            List <KeyValuePair <ParticipantId, AccessCounter> > resources = null;
            KeyValuePair <ParticipantId, AccessCounter>?        manager;

            CollateParticipants(transactionInfo.Participants, out writeParticipants, out resources, out manager);
            try
            {
                TransactionalStatus status = (writeParticipants == null)
                    ? await CommitReadOnlyTransaction(transactionInfo, resources)
                    : await CommitReadWriteTransaction(transactionInfo, writeParticipants, resources, manager.Value);

                if (status == TransactionalStatus.Ok)
                {
                    this.statistics.TrackTransactionSucceeded();
                }
                else
                {
                    this.statistics.TrackTransactionFailed();
                }
                return(status);
            }
            catch (Exception)
            {
                this.statistics.TrackTransactionFailed();
                throw;
            }
        }
Exemple #13
0
        public Task Cancel(Guid transactionId, DateTime timeStamp, TransactionalStatus status)
        {
            // find in queue
            var pos = commitQueue.Find(transactionId, timeStamp);

            if (pos == -1)
            {
                return(Task.CompletedTask); // must have already been cancelled
            }
            storageBatch.Cancel(commitQueue[pos].SequenceNumber);

            storageWorker.Notify();

            AbortCommits(status, pos);

            lockWorker.Notify();

            return(Task.CompletedTask);
        }
Exemple #14
0
        public static OrleansTransactionException ConvertToUserException(this TransactionalStatus status, string TransactionId)
        {
            switch (status)
            {
            case TransactionalStatus.PrepareTimeout:
                return(new OrleansTransactionPrepareTimeoutException(TransactionId));

            case TransactionalStatus.CascadingAbort:
                return(new OrleansCascadingAbortException(TransactionId));

            case TransactionalStatus.BrokenLock:
                return(new OrleansBrokenTransactionLockException(TransactionId, "before prepare"));

            case TransactionalStatus.LockValidationFailed:
                return(new OrleansBrokenTransactionLockException(TransactionId, "when validating accesses during prepare"));

            default:
                return(new OrleansTransactionInDoubtException(TransactionId, $"failure during transaction commit, status={status}"));
            }
        }
Exemple #15
0
        private async Task Bail(TransactionalStatus status, bool force = false)
        {
            await RWLock.AbortExecutingTransactions();

            // abort all entries in the commit queue
            foreach (var entry in commitQueue.Elements)
            {
                await NotifyOfAbort(entry, status);
            }
            commitQueue.Clear();

            this.RWLock.AbortQueuedTransactions();

            if (++failCounter >= 10 || force)
            {
                logger.Debug("StorageWorker triggering grain Deactivation");
                this.deactivate();
            }
            else
            {
                await this.Restore();
            }
        }
Exemple #16
0
        public void NotifyOfCancel(Guid transactionId, DateTime timeStamp, TransactionalStatus status)
        {
            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace("{MethodName}. TransactionId: {TransactionId}, TimeStamp: {TimeStamp} Status: {TransactionalStatus}", nameof(NotifyOfCancel), transactionId, timeStamp, status);
            }

            // find in queue
            var pos = commitQueue.Find(transactionId, timeStamp);

            if (pos == -1)
            {
                return;
            }

            this.storageBatch.Cancel(commitQueue[pos].SequenceNumber);

            AbortCommits(status, pos);

            storageWorker.Notify();

            this.RWLock.Notify();
        }
Exemple #17
0
        private async Task Bail(TransactionalStatus status, bool force = false)
        {
            List <Task> pending = new List <Task>();

            pending.Add(RWLock.AbortExecutingTransactions());
            this.RWLock.AbortQueuedTransactions();

            // abort all entries in the commit queue
            foreach (var entry in commitQueue.Elements)
            {
                pending.Add(NotifyOfAbort(entry, status));
            }
            commitQueue.Clear();

            await Task.WhenAll(pending);

            if (++failCounter >= 10 || force)
            {
                logger.Debug("StorageWorker triggering grain Deactivation");
                this.deactivate();
            }
            await this.Restore();
        }
        private async Task <TransactionalStatus> CommitReadWriteTransaction(TransactionInfo transactionInfo, List <ParticipantId> writeResources, List <KeyValuePair <ParticipantId, AccessCounter> > resources, KeyValuePair <ParticipantId, AccessCounter> manager)
        {
            TransactionalStatus status = TransactionalStatus.Ok;

            try
            {
                foreach (var p in resources)
                {
                    if (p.Key.Equals(manager.Key))
                    {
                        continue;
                    }
                    // one-way prepare message
                    p.Key.Reference.AsReference <ITransactionalResourceExtension>()
                    .Prepare(p.Key.Name, transactionInfo.TransactionId, p.Value, transactionInfo.TimeStamp, manager.Key)
                    .Ignore();
                }

                // wait for the TM to commit the transaction
                status = await manager.Key.Reference.AsReference <ITransactionManagerExtension>()
                         .PrepareAndCommit(manager.Key.Name, transactionInfo.TransactionId, manager.Value, transactionInfo.TimeStamp, writeResources, resources.Count);
            }
            catch (TimeoutException)
            {
                if (logger.IsEnabled(LogLevel.Debug))
                {
                    logger.Debug($"{stopwatch.Elapsed.TotalMilliseconds:f2} timeout {transactionInfo.TransactionId} on CommitReadWriteTransaction");
                }
                status = TransactionalStatus.TMResponseTimeout;
            }
            catch (Exception ex)
            {
                if (logger.IsEnabled(LogLevel.Debug))
                {
                    logger.Debug($"{stopwatch.Elapsed.TotalMilliseconds:f2} failure {transactionInfo.TransactionId} CommitReadWriteTransaction");
                }
                this.logger.LogWarning(ex, "Unknown error while commiting transaction {TransactionId}", transactionInfo.TransactionId);
                status = TransactionalStatus.PresumedAbort;
            }

            if (status != TransactionalStatus.Ok)
            {
                try
                {
                    if (logger.IsEnabled(LogLevel.Debug))
                    {
                        logger.Debug($"{stopwatch.Elapsed.TotalMilliseconds:f2} failed {transactionInfo.TransactionId} with status={status}");
                    }

                    // notify participants
                    if (status.DefinitelyAborted())
                    {
                        await Task.WhenAll(writeResources
                                           .Where(p => !p.Equals(manager.Key))
                                           .Select(p => p.Reference.AsReference <ITransactionalResourceExtension>()
                                                   .Cancel(p.Name, transactionInfo.TransactionId, transactionInfo.TimeStamp, status)));
                    }
                }
                catch (Exception ex)
                {
                    if (logger.IsEnabled(LogLevel.Debug))
                    {
                        logger.Debug($"{stopwatch.Elapsed.TotalMilliseconds:f2} failure aborting {transactionInfo.TransactionId} CommitReadWriteTransaction");
                    }
                    this.logger.LogWarning(ex, "Failed to abort transaction {TransactionId}", transactionInfo.TransactionId);
                }
            }


            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace($"{stopwatch.Elapsed.TotalMilliseconds:f2} finish {transactionInfo.TransactionId}");
            }

            return(status);
        }
 public Task Cancel(string resourceId, Guid transactionId, DateTime timeStamp, TransactionalStatus status)
 {
     return(GetResource(resourceId).Cancel(transactionId, timeStamp, status));
 }
Exemple #20
0
 public Task Prepared(Guid transactionId, DateTime timeStamp, ParticipantId resource, TransactionalStatus status)
 {
     this.queue.NotifyOfPrepared(transactionId, timeStamp, status);
     return(Task.CompletedTask);
 }
Exemple #21
0
        private async Task StorageWork()
        {
            try
            {
                if (problemFlag != TransactionalStatus.Ok)
                {
                    RWLock.AbortExecutingTransactions();

                    // abort all entries in the commit queue
                    foreach (var entry in commitQueue.Elements)
                    {
                        NotifyOfAbort(entry, problemFlag);
                    }
                    commitQueue.Clear();

                    if (problemFlag == TransactionalStatus.StorageConflict)
                    {
                        logger.Debug("deactivating after storage conflict");
                        this.deactivate();
                        this.RWLock.AbortQueuedTransactions();
                    }
                    else
                    {
                        logger.Debug($"restoring state after status={problemFlag}");
                        // recover, clear storageFlag, then allow next queued transaction(s) to enter lock
                        await NotifyOfRestore();
                    }
                }
                else
                {
                    // count committable entries at the bottom of the commit queue
                    int committableEntries = 0;
                    while (committableEntries < commitQueue.Count && commitQueue[committableEntries].ReadyToCommit)
                    {
                        committableEntries++;
                    }

                    // process all committable entries, assembling a storage batch
                    if (committableEntries > 0)
                    {
                        // process all committable entries, adding storage events to the storage batch
                        CollectEventsForBatch(committableEntries);
                        if (problemFlag != TransactionalStatus.Ok)
                        {
                            return;
                        }

                        if (logger.IsEnabled(LogLevel.Debug))
                        {
                            var r = commitQueue.Count > committableEntries ? commitQueue[committableEntries].ToString() : "";
                            logger.Debug($"batchcommit={committableEntries} leave={commitQueue.Count - committableEntries} {r}");
                        }
                    }
                    else
                    {
                        // send or re-send messages and detect timeouts
                        CheckProgressOfCommitQueue();
                    }

                    // store the current storage batch, if it is not empty
                    StorageBatch <TState> batchBeingSentToStorage = null;
                    if (this.storageBatch.BatchSize > 0)
                    {
                        // get the next batch in place so it can be filled while we store the old one
                        batchBeingSentToStorage = this.storageBatch;
                        this.storageBatch       = new StorageBatch <TState>(batchBeingSentToStorage);

                        // perform the actual store, and record the e-tag
                        this.storageBatch.ETag = await batchBeingSentToStorage.Store(storage);
                    }

                    if (committableEntries > 0)
                    {
                        // update stable state
                        var lastCommittedEntry = commitQueue[committableEntries - 1];
                        this.stableState          = lastCommittedEntry.State;
                        this.stableSequenceNumber = lastCommittedEntry.SequenceNumber;
                        if (logger.IsEnabled(LogLevel.Trace))
                        {
                            logger.Trace($"Stable state version: {this.stableSequenceNumber}");
                        }


                        // remove committed entries from commit queue
                        commitQueue.RemoveFromFront(committableEntries);
                        storageWorker.Notify();  // we have to re-check for work
                    }

                    if (batchBeingSentToStorage != null)
                    {
                        batchBeingSentToStorage.RunFollowUpActions();
                        storageWorker.Notify();  // we have to re-check for work
                    }
                }
            }
            catch (InconsistentStateException e)
            {
                logger.Warn(888, $"reload from storage triggered by e-tag mismatch {e}");

                problemFlag = TransactionalStatus.StorageConflict;
            }
            catch (Exception e)
            {
                logger.Warn(888, $"exception in storageWorker", e);

                problemFlag = TransactionalStatus.UnknownException;
            } finally
            {
                if (problemFlag == TransactionalStatus.Ok)
                {
                    this.failCounter = 0;
                }
                else
                {
                    // after exceptions, we try again, but with limits
                    if (++failCounter < 10)
                    {
                        await Task.Delay(100);

                        // this restarts the worker, which sees the problem flag and recovers.
                        storageWorker.Notify();
                    }
                    else
                    {
                        // bail out
                        logger.Warn(999, $"storageWorker is bailing out");
                    }
                }
            }
        }
Exemple #22
0
        private async Task Restore()
        {
            TransactionalStorageLoadResponse <TState> loadresponse = await storage.Load();

            this.storageBatch = new StorageBatch <TState>(loadresponse, this.serializerSettings);

            this.stableState          = loadresponse.CommittedState;
            this.stableSequenceNumber = loadresponse.CommittedSequenceId;

            if (logger.IsEnabled(LogLevel.Debug))
            {
                logger.Debug($"Load v{this.stableSequenceNumber} {loadresponse.PendingStates.Count}p {storageBatch.MetaData.CommitRecords.Count}c");
            }

            // ensure clock is consistent with loaded state
            this.Clock.Merge(storageBatch.MetaData.TimeStamp);

            // resume prepared transactions (not TM)
            foreach (var pr in loadresponse.PendingStates.OrderBy(ps => ps.TimeStamp))
            {
                if (pr.SequenceId > loadresponse.CommittedSequenceId && pr.TransactionManager != null)
                {
                    if (logger.IsEnabled(LogLevel.Debug))
                    {
                        logger.Debug($"recover two-phase-commit {pr.TransactionId}");
                    }

                    ParticipantId tm = JsonConvert.DeserializeObject <ParticipantId>(pr.TransactionManager, this.serializerSettings);

                    commitQueue.Add(new TransactionRecord <TState>()
                    {
                        Role                        = CommitRole.RemoteCommit,
                        TransactionId               = Guid.Parse(pr.TransactionId),
                        Timestamp                   = pr.TimeStamp,
                        State                       = pr.State,
                        SequenceNumber              = pr.SequenceId,
                        TransactionManager          = tm,
                        PrepareIsPersisted          = true,
                        LastSent                    = default(DateTime),
                        ConfirmationResponsePromise = null,
                        NumberWrites                = 1 // was a writing transaction
                    });
                    this.stableSequenceNumber = pr.SequenceId;
                }
            }

            // resume committed transactions (on TM)
            foreach (var kvp in storageBatch.MetaData.CommitRecords)
            {
                if (logger.IsEnabled(LogLevel.Debug))
                {
                    logger.Debug($"recover commit confirmation {kvp.Key}");
                }

                confirmationTasks.Add(kvp.Key, new TransactionRecord <TState>()
                {
                    Role              = CommitRole.LocalCommit,
                    TransactionId     = kvp.Key,
                    Timestamp         = kvp.Value.Timestamp,
                    WriteParticipants = kvp.Value.WriteParticipants
                });
            }

            // clear the problem flag
            problemFlag = TransactionalStatus.Ok;

            // check for work
            this.confirmationWorker.Notify();
            this.storageWorker.Notify();
            this.RWLock.Notify();
        }
Exemple #23
0
 public PreparedMessages(TransactionalStatus status)
 {
     this.Status = status;
 }
Exemple #24
0
        public void NotifyOfAbort(TransactionRecord <TState> entry, TransactionalStatus status)
        {
            switch (entry.Role)
            {
            case CommitRole.NotYetDetermined:
            {
                // cannot notify anyone. TA will detect broken lock during prepare.
                break;
            }

            case CommitRole.RemoteCommit:
            {
                if (logger.IsEnabled(LogLevel.Trace))
                {
                    logger.Trace($"aborting RemoteCommitEntry {entry.Timestamp:o} status={status}");
                }

                entry.ConfirmationResponsePromise?.TrySetException(new OrleansException($"Confirm failed: Status {status}"));

                if (entry.LastSent.HasValue)
                {
                    return;         // cannot abort anymore if we already sent prepare-ok message
                }
                entry.TransactionManager.Reference.AsReference <ITransactionManagerExtension>()
                .Prepared(entry.TransactionManager.Name, entry.TransactionId, entry.Timestamp, resource, status)
                .Ignore();
                break;
            }

            case CommitRole.LocalCommit:
            {
                if (logger.IsEnabled(LogLevel.Trace))
                {
                    logger.Trace($"aborting LocalCommitEntry {entry.Timestamp:o} status={status}");
                }

                // reply to transaction agent
                entry.PromiseForTA.TrySetResult(status);

                // tell remote participants
                foreach (var p in entry.WriteParticipants)
                {
                    if (!p.Equals(resource))
                    {
                        p.Reference.AsReference <ITransactionalResourceExtension>()
                        .Cancel(p.Name, entry.TransactionId, entry.Timestamp, status)
                        .Ignore();
                    }
                }

                break;
            }

            case CommitRole.ReadOnly:
            {
                if (logger.IsEnabled(LogLevel.Trace))
                {
                    logger.Trace($"aborting ReadEntry {entry.Timestamp:o} status={status}");
                }

                // reply to transaction agent
                entry.PromiseForTA.TrySetResult(status);

                break;
            }

            default:
            {
                logger.LogError(777, "internal error: impossible case {CommitRole}", entry.Role);
                throw new NotSupportedException($"{entry.Role} is not a supported CommitRole.");
            }
            }
        }
        private async Task <TransactionalStatus> CommitReadOnlyTransaction(TransactionInfo transactionInfo, List <KeyValuePair <ParticipantId, AccessCounter> > resources)
        {
            TransactionalStatus status = TransactionalStatus.Ok;
            var tasks = new List <Task <TransactionalStatus> >();

            try
            {
                foreach (KeyValuePair <ParticipantId, AccessCounter> resource in resources)
                {
                    tasks.Add(resource.Key.Reference.AsReference <ITransactionalResourceExtension>()
                              .CommitReadOnly(resource.Key.Name, transactionInfo.TransactionId, resource.Value, transactionInfo.TimeStamp));
                }

                // wait for all responses
                TransactionalStatus[] results = await Task.WhenAll(tasks);

                // examine the return status
                foreach (var s in results)
                {
                    if (s != TransactionalStatus.Ok)
                    {
                        status = s;
                        if (logger.IsEnabled(LogLevel.Debug))
                        {
                            logger.Debug($"{stopwatch.Elapsed.TotalMilliseconds:f2} fail {transactionInfo.TransactionId} prepare response status={status}");
                        }
                        break;
                    }
                }
            }
            catch (TimeoutException)
            {
                if (logger.IsEnabled(LogLevel.Debug))
                {
                    logger.Debug($"{stopwatch.Elapsed.TotalMilliseconds:f2} timeout {transactionInfo.TransactionId} on CommitReadOnly");
                }
                status = TransactionalStatus.ParticipantResponseTimeout;
            }
            catch (Exception ex)
            {
                if (logger.IsEnabled(LogLevel.Debug))
                {
                    logger.Debug($"{stopwatch.Elapsed.TotalMilliseconds:f2} failure {transactionInfo.TransactionId} CommitReadOnly");
                }
                this.logger.LogWarning(ex, "Unknown error while commiting readonly transaction {TransactionId}", transactionInfo.TransactionId);
                status = TransactionalStatus.PresumedAbort;
            }

            if (status != TransactionalStatus.Ok)
            {
                try
                {
                    await Task.WhenAll(resources.Select(r => r.Key.Reference.AsReference <ITransactionalResourceExtension>()
                                                        .Abort(r.Key.Name, transactionInfo.TransactionId)));
                }
                catch (Exception ex)
                {
                    if (logger.IsEnabled(LogLevel.Debug))
                    {
                        logger.Debug($"{stopwatch.Elapsed.TotalMilliseconds:f2} failure aborting {transactionInfo.TransactionId} CommitReadOnly");
                    }
                    this.logger.LogWarning(ex, "Failed to abort readonly transaction {TransactionId}", transactionInfo.TransactionId);
                }
            }

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace($"{stopwatch.Elapsed.TotalMilliseconds:f2} finish (reads only) {transactionInfo.TransactionId}");
            }

            return(status);
        }
Exemple #26
0
        private void NotifyOfAbort(TransactionRecord <TState> entry, TransactionalStatus status)
        {
            switch (entry.Role)
            {
            case CommitRole.NotYetDetermined:
            {
                // cannot notify anyone. TA will detect broken lock during prepare.
                break;
            }

            case CommitRole.RemoteCommit:
            {
                if (logger.IsEnabled(LogLevel.Trace))
                {
                    logger.Trace($"aborting RemoteCommitEntry {entry.Timestamp:o} status={status}");
                }

                if (entry.LastSent.HasValue)
                {
                    return;         // cannot abort anymore if we already sent prepare-ok message
                }
                entry.TransactionManager.Prepared(entry.TransactionId, entry.Timestamp, thisParticipant, status).Ignore();
                break;
            }

            case CommitRole.LocalCommit:
            {
                if (logger.IsEnabled(LogLevel.Trace))
                {
                    logger.Trace($"aborting LocalCommitEntry {entry.Timestamp:o} status={status}");
                }

                // reply to transaction agent
                entry.PromiseForTA.TrySetResult(status);

                // tell remote participants
                foreach (var p in entry.WriteParticipants)
                {
                    if (p != thisParticipant)
                    {
                        p.Cancel(entry.TransactionId, entry.Timestamp, status).Ignore();
                    }
                }

                break;
            }

            case CommitRole.ReadOnly:
            {
                if (logger.IsEnabled(LogLevel.Trace))
                {
                    logger.Trace($"aborting ReadEntry {entry.Timestamp:o} status={status}");
                }

                // reply to transaction agent
                entry.PromiseForTA.TrySetResult(status);

                break;
            }

            default:
            {
                logger.LogError(777, "internal error: impossible case {CommitRole}", entry.Role);
                throw new NotSupportedException($"{entry.Role} is not a supported CommitRole.");
            }
            }
        }
        /// <summary>
        /// called on activation, and when recovering from storage conflicts or other exceptions.
        /// </summary>
        private async Task Restore()
        {
            // start the load
            var loadtask = this.storage.Load();

            // abort active transactions, without waking up waiters just yet
            AbortExecutingTransactions("due to restore");

            // abort all entries in the commit queue
            foreach (var entry in commitQueue.Elements)
            {
                NotifyOfAbort(entry, problemFlag);
            }
            commitQueue.Clear();

            var loadresponse = await loadtask;

            storageBatch = new StorageBatch <TState>(loadresponse);

            stableState          = loadresponse.CommittedState;
            stableSequenceNumber = loadresponse.CommittedSequenceId;

            if (logger.IsEnabled(LogLevel.Debug))
            {
                logger.Debug($"Load v{stableSequenceNumber} {loadresponse.PendingStates.Count}p {storageBatch.MetaData.CommitRecords.Count}c");
            }

            // ensure clock is consistent with loaded state
            MergeClock(storageBatch.MetaData.TimeStamp);

            // resume prepared transactions (not TM)
            foreach (var pr in loadresponse.PendingStates.OrderBy(ps => ps.TimeStamp))
            {
                if (pr.SequenceId > stableSequenceNumber && pr.TransactionManager != null)
                {
                    if (logger.IsEnabled(LogLevel.Debug))
                    {
                        logger.Debug($"recover two-phase-commit {pr.TransactionId}");
                    }

                    var tm = (pr.TransactionManager == null) ? null :
                             (ITransactionParticipant)JsonConvert.DeserializeObject <ITransactionParticipant>(pr.TransactionManager, MetaData.SerializerSettings);

                    commitQueue.Add(new TransactionRecord <TState>()
                    {
                        Role                        = CommitRole.RemoteCommit,
                        TransactionId               = Guid.Parse(pr.TransactionId),
                        Timestamp                   = pr.TimeStamp,
                        State                       = pr.State,
                        TransactionManager          = tm,
                        PrepareIsPersisted          = true,
                        LastSent                    = default(DateTime),
                        ConfirmationResponsePromise = null
                    });
                }
            }

            // resume committed transactions (on TM)
            foreach (var kvp in storageBatch.MetaData.CommitRecords)
            {
                if (logger.IsEnabled(LogLevel.Debug))
                {
                    logger.Debug($"recover commit confirmation {kvp.Key}");
                }

                confirmationTasks.Add(kvp.Key, new TransactionRecord <TState>()
                {
                    Role              = CommitRole.LocalCommit,
                    TransactionId     = kvp.Key,
                    Timestamp         = kvp.Value.Timestamp,
                    WriteParticipants = kvp.Value.WriteParticipants
                });
            }

            // clear the problem flag
            problemFlag = TransactionalStatus.Ok;

            // check for work
            confirmationWorker.Notify();
            storageWorker.Notify();
            lockWorker.Notify();
        }
 public Task Prepared(Guid transactionId, DateTime timeStamp, ITransactionParticipant participant, TransactionalStatus status)
 {
     return(extension.Prepared(resourceId, transactionId, timeStamp, participant, status));
 }
 public Task Prepared(string resourceId, Guid transactionId, DateTime timestamp, ParticipantId resource, TransactionalStatus status)
 {
     return(GetManager(resourceId).Prepared(transactionId, timestamp, resource, status));
 }
 public Task Cancel(Guid transactionId, DateTime timeStamp, TransactionalStatus status)
 {
     return(extension.Cancel(resourceId, transactionId, timeStamp, status));
 }