Beispiel #1
0
        internal async Task ProcessMessageAsync(ProcessMessageEventArgs args)
        {
            EnsureIsRunning();

            _concurrencyUpdateManager?.MessageProcessed();

            using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(args.CancellationToken, _cancellationTokenSource.Token))
            {
                var actions = new ServiceBusMessageActions(args);
                if (!await _messageProcessor.Value.BeginProcessingMessageAsync(actions, args.Message, linkedCts.Token).ConfigureAwait(false))
                {
                    return;
                }

                var receiveActions           = new ServiceBusReceiveActions(args);
                ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateSingle(args.Message, actions, receiveActions, _client.Value);

                TriggeredFunctionData data = input.GetTriggerFunctionData();

                FunctionResult result = await _triggerExecutor.TryExecuteAsync(data, linkedCts.Token).ConfigureAwait(false);

                try
                {
                    await _messageProcessor.Value.CompleteProcessingMessageAsync(actions, args.Message, result, linkedCts.Token)
                    .ConfigureAwait(false);
                }
                finally
                {
                    receiveActions.EndExecutionScope();
                }
            }
        }
Beispiel #2
0
        public static async Task Run(
            [ServiceBusTrigger("<queue_name>", Connection = "<connection_name>", IsSessionsEnabled = true)]
            ServiceBusReceivedMessage message,
            ServiceBusMessageActions messageActions,
            ServiceBusReceiveActions receiveActions)
        {
            if (message.MessageId == "1")
            {
                await messageActions.DeadLetterMessageAsync(message);
            }
            else
            {
                await messageActions.CompleteMessageAsync(message);

                // attempt to receive additional messages in this session
                await receiveActions.ReceiveMessagesAsync(maxMessages : 10);
            }
        }
Beispiel #3
0
        private async Task RunBatchReceiveLoopAsync(CancellationToken cancellationToken)
        {
            ServiceBusClient   sessionClient = null;
            ServiceBusReceiver receiver      = null;

            if (_isSessionsEnabled)
            {
                sessionClient = _client.Value;
            }
            else
            {
                receiver = _batchReceiver.Value;
            }

            // The batch receive loop below only executes functions at a concurrency level of 1,
            // so we don't need to do anything special when DynamicConcurrency is enabled. If in
            // the future we make this loop concurrent, we'll have to check with ConcurrencyManager.
            while (true)
            {
                try
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        _logger.LogInformation($"Message processing has been stopped or cancelled ({_details.Value})");
                        return;
                    }

                    if (_isSessionsEnabled && (receiver == null || receiver.IsClosed))
                    {
                        try
                        {
                            receiver = await sessionClient.AcceptNextSessionAsync(
                                _entityPath,
                                new ServiceBusSessionReceiverOptions
                            {
                                PrefetchCount = _serviceBusOptions.PrefetchCount
                            },
                                cancellationToken).ConfigureAwait(false);
                        }
                        catch (ServiceBusException ex)
                            when(ex.Reason == ServiceBusFailureReason.ServiceTimeout)
                            {
                                // it's expected if the entity is empty, try next time
                                continue;
                            }
                    }

                    IReadOnlyList <ServiceBusReceivedMessage> messages =
                        await receiver.ReceiveMessagesAsync(
                            _serviceBusOptions.MaxMessageBatchSize,
                            cancellationToken : cancellationToken).AwaitWithCancellation(cancellationToken);

                    if (messages.Count > 0)
                    {
                        var messageActions = _isSessionsEnabled
                            ? new ServiceBusSessionMessageActions((ServiceBusSessionReceiver)receiver)
                            : new ServiceBusMessageActions(receiver);
                        var receiveActions = new ServiceBusReceiveActions(receiver);

                        ServiceBusReceivedMessage[] messagesArray = messages.ToArray();
                        ServiceBusTriggerInput      input         = ServiceBusTriggerInput.CreateBatch(
                            messagesArray,
                            messageActions,
                            receiveActions,
                            _client.Value);

                        FunctionResult result = await _triggerExecutor.TryExecuteAsync(input.GetTriggerFunctionData(), cancellationToken).ConfigureAwait(false);

                        receiveActions.EndExecutionScope();

                        var processedMessages = messagesArray.Concat(receiveActions.Messages.Keys);
                        // Complete batch of messages only if the execution was successful
                        if (_autoCompleteMessages && result.Succeeded)
                        {
                            List <Task> completeTasks = new List <Task>();
                            foreach (ServiceBusReceivedMessage message in processedMessages)
                            {
                                // skip messages that were settled in the user's function
                                if (input.MessageActions.SettledMessages.ContainsKey(message))
                                {
                                    continue;
                                }

                                // Pass CancellationToken.None to allow autocompletion to finish even when shutting down
                                completeTasks.Add(receiver.CompleteMessageAsync(message, CancellationToken.None));
                            }

                            await Task.WhenAll(completeTasks).ConfigureAwait(false);
                        }
                        else if (!result.Succeeded)
                        {
                            // For failed executions, we abandon the messages regardless of the autoCompleteMessages configuration.
                            // This matches the behavior that happens for single dispatch functions as the processor does the same thing
                            // in the Service Bus SDK.

                            List <Task> abandonTasks = new List <Task>();
                            foreach (ServiceBusReceivedMessage message in processedMessages)
                            {
                                // skip messages that were settled in the user's function
                                if (input.MessageActions.SettledMessages.ContainsKey(message))
                                {
                                    continue;
                                }

                                // Pass CancellationToken.None to allow abandon to finish even when shutting down
                                abandonTasks.Add(receiver.AbandonMessageAsync(message, cancellationToken: CancellationToken.None));
                            }

                            await Task.WhenAll(abandonTasks).ConfigureAwait(false);
                        }

                        if (_isSessionsEnabled)
                        {
                            if (((ServiceBusSessionMessageActions)messageActions).ShouldReleaseSession)
                            {
                                // Use CancellationToken.None to attempt to close the receiver even when shutting down
                                await receiver.CloseAsync(CancellationToken.None).ConfigureAwait(false);
                            }
                        }
                    }
                    else
                    {
                        // Close the session and release the session lock after draining all messages for the accepted session.
                        if (_isSessionsEnabled)
                        {
                            // Use CancellationToken.None to attempt to close the receiver even when shutting down
                            await receiver.CloseAsync(CancellationToken.None).ConfigureAwait(false);
                        }
                    }
                }
                catch (ObjectDisposedException)
                {
                    // Ignore as we are stopping the host
                }
                catch (OperationCanceledException)
                    when(cancellationToken.IsCancellationRequested)
                    {
                        // Ignore as we are stopping the host
                        _logger.LogInformation($"Message processing has been stopped or cancelled ({_details.Value})");
                    }
                catch (Exception ex)
                {
                    // Log another exception
                    _logger.LogError(ex, $"An unhandled exception occurred in the message batch receive loop ({_details.Value})");

                    if (_isSessionsEnabled && receiver != null)
                    {
                        // Attempt to close the session and release session lock to accept a new session on the next loop iteration
                        try
                        {
                            // Use CancellationToken.None to attempt to close the receiver even when shutting down
                            await receiver.CloseAsync(CancellationToken.None).ConfigureAwait(false);
                        }
                        catch
                        {
                            // Best effort
                            receiver = null;
                        }
                    }
                }
            }
        }