Esempio n. 1
0
        public async Task Commit(ITransactionInfo info)
        {
            var transactionInfo = (TransactionInfo)info;

            TransactionsStatisticsGroup.OnTransactionCommitRequest();

            if (transactionInfo.IsReadOnly)
            {
                return;
            }

            var  completion = new TaskCompletionSource <bool>();
            bool canCommit  = true;

            List <Task <bool> > prepareTasks = new List <Task <bool> >(transactionInfo.WriteSet.Count);

            foreach (var g in transactionInfo.WriteSet.Keys)
            {
                TransactionalResourceVersion write = TransactionalResourceVersion.Create(transactionInfo.TransactionId, transactionInfo.WriteSet[g]);
                TransactionalResourceVersion?read  = null;
                if (transactionInfo.ReadSet.ContainsKey(g))
                {
                    read = transactionInfo.ReadSet[g];
                    transactionInfo.ReadSet.Remove(g);
                }
                prepareTasks.Add(g.Prepare(transactionInfo.TransactionId, write, read));
            }

            foreach (var g in transactionInfo.ReadSet.Keys)
            {
                TransactionalResourceVersion read = transactionInfo.ReadSet[g];
                prepareTasks.Add(g.Prepare(transactionInfo.TransactionId, null, read));
            }

            await Task.WhenAll(prepareTasks);

            foreach (var t in prepareTasks)
            {
                if (!t.Result)
                {
                    canCommit = false;
                }
            }

            if (!canCommit)
            {
                TransactionsStatisticsGroup.OnTransactionAborted();
                abortedTransactions.TryAdd(transactionInfo.TransactionId, 0);
                throw new OrleansPrepareFailedException(transactionInfo.TransactionId);
            }
            commitCompletions.TryAdd(transactionInfo.TransactionId, completion);
            transactionCommitQueue.Enqueue(transactionInfo);
            await completion.Task;
        }
Esempio n. 2
0
        private async Task CommitTransactions(List <TransactionInfo> committingTransactions,
                                              HashSet <long> outstandingCommits)
        {
            var stopWatch = Stopwatch.StartNew();

            try
            {
                metrics.BatchCommitTransactionsRequestsCounter++;
                metrics.BatchCommitTransactionsRequestSizeCounter += committingTransactions.Count;
                CommitTransactionsResponse commitResponse;
                try
                {
                    commitResponse = await this.tmService.CommitTransactions(committingTransactions, outstandingCommits);
                }
                finally
                {
                    stopWatch.Stop();
                    metrics.BatchCommitTransactionsRequestLatencyCounter += stopWatch.Elapsed;
                }

                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.Debug(ErrorCode.Transactions_ReceivedTMResponse,
                             "{0} transactions committed. readOnlyTransactionId {1}, abortLowerBound {2}",
                             committingTransactions.Count, ReadOnlyTransactionId, abortLowerBound);
            }
            catch (Exception e)
            {
                logger.Error(ErrorCode.Transactions_TMError, "TM Error", 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();
        }
Esempio n. 3
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.StartTransactions(startingTransactions, startCompletions);
                }

                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);
                            }
                        }
                    }
                }
            }
        }