예제 #1
0
        private async Task StartTransactions(List <TimeSpan> startingTransactions, List <TaskCompletionSource <long> > startCompletions)
        {
            try
            {
                StartTransactionsResponse startResponse = await this.tmService.StartTransactions(startingTransactions);

                List <long> startedIds = startResponse.TransactionId;

                // reply to clients with results
                for (int i = 0; i < startCompletions.Count; i++)
                {
                    TransactionsStatisticsGroup.OnTransactionStarted();
                    startCompletions[i].SetResult(startedIds[i]);
                }

                // Refresh cached values using new values from TM.
                this.ReadOnlyTransactionId = Math.Max(this.ReadOnlyTransactionId, startResponse.ReadOnlyTransactionId);
                this.abortLowerBound       = Math.Max(this.abortLowerBound, startResponse.AbortLowerBound);
                logger.Verbose(ErrorCode.Transactions_ReceivedTMResponse, "{0} Transactions started. readOnlyTransactionId {1}, abortLowerBound {2}", startingTransactions.Count, ReadOnlyTransactionId, abortLowerBound);
            }
            catch (Exception e)
            {
                logger.Error(ErrorCode.Transactions_TMError, "Transaction manager failed to start transactions.", e);

                foreach (var completion in startCompletions)
                {
                    TransactionsStatisticsGroup.OnTransactionStartFailed();
                    completion.SetException(new OrleansStartTransactionFailedException(e));
                }
            }

            startingTransactions.Clear();
            startCompletions.Clear();
        }
예제 #2
0
        private async Task ProcessRequests(object args)
        {
            // NOTE: This code is a bit complicated because we want to issue both start and commit requests,
            // but wait for each one separately in its own continuation. This can be significantly simplified
            // if we can register a separate timer for start and commit.

            List <TransactionInfo> committingTransactions        = new List <TransactionInfo>();
            List <TimeSpan>        startingTransactions          = new List <TimeSpan>();
            List <TaskCompletionSource <long> > startCompletions = new List <TaskCompletionSource <long> >();

            while (transactionCommitQueue.Count > 0 || transactionStartQueue.Count > 0 || outstandingCommits.Count > 0)
            {
                var initialAbortLowerBound = this.abortLowerBound;

                await Task.Yield();
                await WaitForWork();

                int startCount = transactionStartQueue.Count;
                while (startCount > 0 && startTransactionsTask.IsCompleted)
                {
                    Tuple <TimeSpan, TaskCompletionSource <long> > elem;
                    transactionStartQueue.TryDequeue(out elem);
                    startingTransactions.Add(elem.Item1);
                    startCompletions.Add(elem.Item2);

                    startCount--;
                }

                int commitCount = transactionCommitQueue.Count;
                while (commitCount > 0 && commitTransactionsTask.IsCompleted)
                {
                    TransactionInfo elem;
                    transactionCommitQueue.TryDequeue(out elem);
                    committingTransactions.Add(elem);
                    outstandingCommits.Add(elem.TransactionId);

                    commitCount--;
                }


                if (startingTransactions.Count > 0 && startTransactionsTask.IsCompleted)
                {
                    logger.Verbose(ErrorCode.Transactions_SendingTMRequest, "Calling TM to start {0} transactions", startingTransactions.Count);

                    startTransactionsTask = this.tmService.StartTransactions(startingTransactions).ContinueWith(
                        async startRequest =>
                    {
                        try
                        {
                            var startResponse = await startRequest;
                            var startedIds    = startResponse.TransactionId;

                            // reply to clients with results
                            for (int i = 0; i < startCompletions.Count; i++)
                            {
                                TransactionsStatisticsGroup.OnTransactionStarted();
                                startCompletions[i].SetResult(startedIds[i]);
                            }

                            // Refresh cached values using new values from TM.
                            this.ReadOnlyTransactionId = Math.Max(this.ReadOnlyTransactionId, startResponse.ReadOnlyTransactionId);
                            this.abortLowerBound       = Math.Max(this.abortLowerBound, startResponse.AbortLowerBound);
                            logger.Verbose(ErrorCode.Transactions_ReceivedTMResponse, "{0} transactions started. readOnlyTransactionId {1}, abortLowerBound {2}", startingTransactions.Count, ReadOnlyTransactionId, abortLowerBound);
                        }
                        catch (Exception e)
                        {
                            logger.Error(ErrorCode.Transactions_TMError, "", e);

                            foreach (var completion in startCompletions)
                            {
                                TransactionsStatisticsGroup.OnTransactionStartFailed();
                                completion.SetException(new OrleansStartTransactionFailedException(e));
                            }
                        }

                        startingTransactions.Clear();
                        startCompletions.Clear();
                    });
                }

                if ((committingTransactions.Count > 0 || outstandingCommits.Count > 0) && commitTransactionsTask.IsCompleted)
                {
                    logger.Verbose(ErrorCode.Transactions_SendingTMRequest, "Calling TM to commit {0} transactions", committingTransactions.Count);

                    commitTransactionsTask = this.tmService.CommitTransactions(committingTransactions, outstandingCommits).ContinueWith(
                        async commitRequest =>
                    {
                        try
                        {
                            var commitResponse = await commitRequest;
                            var commitResults  = commitResponse.CommitResult;

                            // reply to clients with the outcomes we received from the TM.
                            foreach (var completedId in commitResults.Keys)
                            {
                                outstandingCommits.Remove(completedId);

                                TaskCompletionSource <bool> completion;
                                if (commitCompletions.TryRemove(completedId, out completion))
                                {
                                    if (commitResults[completedId].Success)
                                    {
                                        TransactionsStatisticsGroup.OnTransactionCommitted();
                                        completion.SetResult(true);
                                    }
                                    else
                                    {
                                        if (commitResults[completedId].AbortingException != null)
                                        {
                                            TransactionsStatisticsGroup.OnTransactionAborted();
                                            completion.SetException(commitResults[completedId].AbortingException);
                                        }
                                        else
                                        {
                                            TransactionsStatisticsGroup.OnTransactionInDoubt();
                                            completion.SetException(new OrleansTransactionInDoubtException(completedId));
                                        }
                                    }
                                }
                            }

                            // Refresh cached values using new values from TM.
                            this.ReadOnlyTransactionId = Math.Max(this.ReadOnlyTransactionId, commitResponse.ReadOnlyTransactionId);
                            this.abortLowerBound       = Math.Max(this.abortLowerBound, commitResponse.AbortLowerBound);
                            logger.Verbose(ErrorCode.Transactions_ReceivedTMResponse, "{0} transactions committed. readOnlyTransactionId {1}, abortLowerBound {2}", committingTransactions.Count, ReadOnlyTransactionId, abortLowerBound);
                        }
                        catch (Exception e)
                        {
                            logger.Error(ErrorCode.Transactions_TMError, "", e);

                            // Propagate the exception to every transaction in the request.
                            foreach (var tx in committingTransactions)
                            {
                                TransactionsStatisticsGroup.OnTransactionInDoubt();

                                TaskCompletionSource <bool> completion;
                                if (commitCompletions.TryRemove(tx.TransactionId, out completion))
                                {
                                    outstandingCommits.Remove(tx.TransactionId);
                                    completion.SetException(new OrleansTransactionInDoubtException(tx.TransactionId));
                                }
                            }
                        }

                        committingTransactions.Clear();
                    });

                    // Removed transactions below the abort lower bound.
                    if (this.abortLowerBound != initialAbortLowerBound)
                    {
                        foreach (var aborted in this.abortedTransactions)
                        {
                            if (aborted.Key < this.abortLowerBound)
                            {
                                long ignored;
                                this.abortedTransactions.TryRemove(aborted.Key, out ignored);
                            }
                        }
                    }
                }
            }
        }