Example #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();
                }
            }
        }
Example #2
0
        public void GetBindingData_SingleDispatch_ReturnsExpectedValue()
        {
            IDictionary <string, object> userProps = new Dictionary <string, object>();

            userProps.Add(new KeyValuePair <string, object>("prop1", "value1"));
            userProps.Add(new KeyValuePair <string, object>("prop2", "value2"));
            var message = CreateMessageWithSystemProperties(applicationProperties: userProps);

            var input       = ServiceBusTriggerInput.CreateSingle(message);
            var strategy    = new ServiceBusTriggerBindingStrategy();
            var bindingData = strategy.GetBindingData(input);

            Assert.AreEqual(15, bindingData.Count);

            Assert.AreSame(input.Receiver, bindingData["MessageReceiver"]);
            Assert.AreSame(input.SessionReceiver, bindingData["MessageSession"]);
            Assert.AreEqual(message.LockToken, bindingData["LockToken"]);
            Assert.AreEqual(message.SequenceNumber, bindingData["SequenceNumber"]);
            Assert.AreEqual(message.DeliveryCount, bindingData["DeliveryCount"]);
            Assert.AreSame(message.DeadLetterSource, bindingData["DeadLetterSource"]);
            Assert.AreEqual(message.ExpiresAt, bindingData["ExpiresAtUtc"]);
            Assert.AreEqual(message.EnqueuedTime, bindingData["EnqueuedTimeUtc"]);
            Assert.AreSame(message.MessageId, bindingData["MessageId"]);
            Assert.AreSame(message.ContentType, bindingData["ContentType"]);
            Assert.AreSame(message.ReplyTo, bindingData["ReplyTo"]);
            Assert.AreSame(message.To, bindingData["To"]);
            Assert.AreSame(message.Subject, bindingData["Label"]);
            Assert.AreSame(message.CorrelationId, bindingData["CorrelationId"]);

            IDictionary <string, object> bindingDataUserProps = bindingData["ApplicationProperties"] as IDictionary <string, object>;

            Assert.NotNull(bindingDataUserProps);
            Assert.AreEqual("value1", bindingDataUserProps["prop1"]);
            Assert.AreEqual("value2", bindingDataUserProps["prop2"]);
        }
Example #3
0
        public void GetBindingData_MultipleDispatch_ReturnsExpectedValue()
        {
            var messages = new ServiceBusReceivedMessage[3]
            {
                CreateMessageWithSystemProperties("Event 1"),
                CreateMessageWithSystemProperties("Event 2"),
                CreateMessageWithSystemProperties("Event 3"),
            };

            var input       = ServiceBusTriggerInput.CreateBatch(messages, null, null);
            var strategy    = new ServiceBusTriggerBindingStrategy();
            var bindingData = strategy.GetBindingData(input);

            Assert.AreEqual(BindingContractCount, bindingData.Count);
            Assert.AreSame(input.MessageActions, bindingData["MessageReceiver"]);
            Assert.AreSame(input.MessageActions, bindingData["MessageSession"]);
            Assert.AreSame(input.MessageActions, bindingData["MessageActions"]);
            Assert.AreSame(input.MessageActions, bindingData["SessionActions"]);

            // verify an array was created for each binding data type
            Assert.AreEqual(messages.Length, ((int[])bindingData["DeliveryCountArray"]).Length);
            Assert.AreEqual(messages.Length, ((string[])bindingData["DeadLetterSourceArray"]).Length);
            Assert.AreEqual(messages.Length, ((string[])bindingData["LockTokenArray"]).Length);
            Assert.AreEqual(messages.Length, ((DateTime[])bindingData["ExpiresAtUtcArray"]).Length);
            Assert.AreEqual(messages.Length, ((DateTime[])bindingData["EnqueuedTimeUtcArray"]).Length);
            Assert.AreEqual(messages.Length, ((string[])bindingData["MessageIdArray"]).Length);
            Assert.AreEqual(messages.Length, ((string[])bindingData["ContentTypeArray"]).Length);
            Assert.AreEqual(messages.Length, ((string[])bindingData["ReplyToArray"]).Length);
            Assert.AreEqual(messages.Length, ((long[])bindingData["SequenceNumberArray"]).Length);
            Assert.AreEqual(messages.Length, ((string[])bindingData["ToArray"]).Length);
            Assert.AreEqual(messages.Length, ((string[])bindingData["SubjectArray"]).Length);
            Assert.AreEqual(messages.Length, ((string[])bindingData["CorrelationIdArray"]).Length);
            Assert.AreEqual(messages.Length, ((IDictionary <string, object>[])bindingData["ApplicationPropertiesArray"]).Length);
        }
        internal async Task ProcessSessionMessageAsync(ProcessSessionMessageEventArgs args)
        {
            _concurrencyUpdateManager?.MessageProcessed();

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

                ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateSingle(args.Message, actions, _client.Value);

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

                if (actions.ShouldReleaseSession)
                {
                    args.ReleaseSession();
                }

                await _sessionMessageProcessor.Value.CompleteProcessingMessageAsync(actions, args.Message, result, linkedCts.Token).ConfigureAwait(false);
            }
        }
Example #5
0
        public void BindSingle_Returns_Exptected_Message()
        {
            string data = "123";

            var strategy = new ServiceBusTriggerBindingStrategy();
            ServiceBusTriggerInput triggerInput = strategy.ConvertFromString(data);

            var contract = strategy.GetBindingData(triggerInput);

            ServiceBusReceivedMessage single = strategy.BindSingle(triggerInput, null);
            string body = single.Body.ToString();

            Assert.AreEqual(data, body);
            Assert.Null(contract["MessageReceiver"]);
            Assert.Null(contract["MessageSession"]);
        }
        internal async Task ProcessMessageAsync(Message message, CancellationToken cancellationToken)
        {
            using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token))
            {
                if (!await _messageProcessor.BeginProcessingMessageAsync(message, linkedCts.Token))
                {
                    return;
                }

                ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateSingle(message);
                input.MessageReceiver = Receiver;

                TriggeredFunctionData data   = input.GetTriggerFunctionData();
                FunctionResult        result = await _triggerExecutor.TryExecuteAsync(data, linkedCts.Token);

                await _messageProcessor.CompleteProcessingMessageAsync(message, result, linkedCts.Token);
            }
        }
Example #7
0
        internal async Task ProcessSessionMessageAsync(ProcessSessionMessageEventArgs args)
        {
            using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(args.CancellationToken, _cancellationTokenSource.Token))
            {
                var actions = new ServiceBusSessionMessageActions(args);
                if (!await _sessionMessageProcessor.BeginProcessingMessageAsync(actions, args.Message, linkedCts.Token).ConfigureAwait(false))
                {
                    return;
                }

                ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateSingle(args.Message);
                input.MessageActions = actions;

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

                await _sessionMessageProcessor.CompleteProcessingMessageAsync(actions, args.Message, result, linkedCts.Token).ConfigureAwait(false);
            }
        }
        internal async Task ProcessSessionMessageAsync(ProcessSessionMessageEventArgs args)
        {
            using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(args.CancellationToken, _cancellationTokenSource.Token))
            {
                ServiceBusSessionReceiver receiver = (ServiceBusSessionReceiver)typeof(ProcessSessionMessageEventArgs).GetField("_sessionReceiver", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(args);

                if (!await _sessionMessageProcessor.BeginProcessingMessageAsync(receiver, args.Message, linkedCts.Token).ConfigureAwait(false))
                {
                    return;
                }

                ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateSingle(args.Message);
                input.SessionReceiver = receiver;

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

                await _sessionMessageProcessor.CompleteProcessingMessageAsync(receiver, args.Message, result, linkedCts.Token).ConfigureAwait(false);
            }
        }
Example #9
0
        public void GetBindingData_MultipleDispatch_ReturnsExpectedValue()
        {
            var messages = new Message[3]
            {
                new Message(Encoding.UTF8.GetBytes("Event 1")),
                new Message(Encoding.UTF8.GetBytes("Event 2")),
                new Message(Encoding.UTF8.GetBytes("Event 3")),
            };

            foreach (var message in messages)
            {
                SystemPropertiesCollection sysProps = GetSystemProperties();
                TestHelpers.SetField(message, "SystemProperties", sysProps);
            }

            var input       = ServiceBusTriggerInput.CreateBatch(messages);
            var strategy    = new ServiceBusTriggerBindingStrategy();
            var bindingData = strategy.GetBindingData(input);

            Assert.Equal(15, bindingData.Count);
            Assert.Same(input.MessageReceiver as MessageReceiver, bindingData["MessageReceiver"]);
            Assert.Same(input.MessageReceiver as IMessageSession, bindingData["MessageSession"]);

            // verify an array was created for each binding data type
            Assert.Equal(messages.Length, ((int[])bindingData["DeliveryCountArray"]).Length);
            Assert.Equal(messages.Length, ((string[])bindingData["DeadLetterSourceArray"]).Length);
            Assert.Equal(messages.Length, ((string[])bindingData["LockTokenArray"]).Length);
            Assert.Equal(messages.Length, ((DateTime[])bindingData["ExpiresAtUtcArray"]).Length);
            Assert.Equal(messages.Length, ((DateTime[])bindingData["EnqueuedTimeUtcArray"]).Length);
            Assert.Equal(messages.Length, ((string[])bindingData["MessageIdArray"]).Length);
            Assert.Equal(messages.Length, ((string[])bindingData["ContentTypeArray"]).Length);
            Assert.Equal(messages.Length, ((string[])bindingData["ReplyToArray"]).Length);
            Assert.Equal(messages.Length, ((long[])bindingData["SequenceNumberArray"]).Length);
            Assert.Equal(messages.Length, ((string[])bindingData["ToArray"]).Length);
            Assert.Equal(messages.Length, ((string[])bindingData["LabelArray"]).Length);
            Assert.Equal(messages.Length, ((string[])bindingData["CorrelationIdArray"]).Length);
            Assert.Equal(messages.Length, ((IDictionary <string, object>[])bindingData["UserPropertiesArray"]).Length);
        }
Example #10
0
        public void GetBindingData_SingleDispatch_ReturnsExpectedValue()
        {
            var message = new Message(new byte[] { });
            SystemPropertiesCollection sysProp = GetSystemProperties();

            TestHelpers.SetField(message, "SystemProperties", sysProp);
            IDictionary <string, object> userProps = new Dictionary <string, object>();

            userProps.Add(new KeyValuePair <string, object>("prop1", "value1"));
            userProps.Add(new KeyValuePair <string, object>("prop2", "value2"));
            TestHelpers.SetField(message, "UserProperties", userProps);

            var input       = ServiceBusTriggerInput.CreateSingle(message);
            var strategy    = new ServiceBusTriggerBindingStrategy();
            var bindingData = strategy.GetBindingData(input);

            Assert.Equal(15, bindingData.Count);  // SystemPropertiesCollection is sealed

            Assert.Same(input.MessageReceiver as MessageReceiver, bindingData["MessageReceiver"]);
            Assert.Same(input.MessageReceiver as IMessageSession, bindingData["MessageSession"]);
            Assert.Equal(message.SystemProperties.LockToken, bindingData["LockToken"]);
            Assert.Equal(message.SystemProperties.SequenceNumber, bindingData["SequenceNumber"]);
            Assert.Equal(message.SystemProperties.DeliveryCount, bindingData["DeliveryCount"]);
            Assert.Same(message.SystemProperties.DeadLetterSource, bindingData["DeadLetterSource"]);
            Assert.Equal(message.ExpiresAtUtc, bindingData["ExpiresAtUtc"]);
            Assert.Same(message.MessageId, bindingData["MessageId"]);
            Assert.Same(message.ContentType, bindingData["ContentType"]);
            Assert.Same(message.ReplyTo, bindingData["ReplyTo"]);
            Assert.Same(message.To, bindingData["To"]);
            Assert.Same(message.Label, bindingData["Label"]);
            Assert.Same(message.CorrelationId, bindingData["CorrelationId"]);

            IDictionary <string, object> bindingDataUserProps = bindingData["UserProperties"] as Dictionary <string, object>;

            Assert.NotNull(bindingDataUserProps);
            Assert.Equal("value1", bindingDataUserProps["prop1"]);
            Assert.Equal("value2", bindingDataUserProps["prop2"]);
        }
Example #11
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;
                        }
                    }
                }
            }
        }
Example #12
0
        internal void StartMessageBatchReceiver(CancellationToken cancellationToken)
        {
            ServiceBusClient   sessionClient = null;
            ServiceBusReceiver receiver      = null;

            if (_isSessionsEnabled)
            {
                sessionClient = _sessionClient.Value;
            }
            else
            {
                receiver = BatchReceiver;
            }

            Task.Run(async() =>
            {
                while (true)
                {
                    try
                    {
                        if (!_started || cancellationToken.IsCancellationRequested)
                        {
                            _logger.LogInformation("Message processing has been stopped or cancelled");
                            return;
                        }

                        if (_isSessionsEnabled && (receiver == null || receiver.IsClosed))
                        {
                            try
                            {
                                receiver = await sessionClient.AcceptNextSessionAsync(_entityPath, new ServiceBusSessionReceiverOptions
                                {
                                    PrefetchCount = _serviceBusOptions.PrefetchCount
                                }).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.MaxMessages).ConfigureAwait(false);

                        if (messages != null)
                        {
                            ServiceBusReceivedMessage[] messagesArray = messages.ToArray();
                            ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateBatch(messagesArray);
                            if (_isSessionsEnabled)
                            {
                                input.MessageActions = new ServiceBusSessionMessageActions((ServiceBusSessionReceiver)receiver);
                            }
                            else
                            {
                                input.MessageActions = new ServiceBusMessageActions(receiver);
                            }
                            FunctionResult result = await _triggerExecutor.TryExecuteAsync(input.GetTriggerFunctionData(), cancellationToken).ConfigureAwait(false);

                            if (cancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            // Complete batch of messages only if the execution was successful
                            if (_serviceBusOptions.AutoCompleteMessages && _started)
                            {
                                if (result.Succeeded)
                                {
                                    List <Task> completeTasks = new List <Task>();
                                    foreach (ServiceBusReceivedMessage message in messagesArray)
                                    {
                                        completeTasks.Add(receiver.CompleteMessageAsync(message));
                                    }
                                    await Task.WhenAll(completeTasks).ConfigureAwait(false);
                                }
                                else
                                {
                                    List <Task> abandonTasks = new List <Task>();
                                    foreach (ServiceBusReceivedMessage message in messagesArray)
                                    {
                                        abandonTasks.Add(receiver.AbandonMessageAsync(message));
                                    }
                                    await Task.WhenAll(abandonTasks).ConfigureAwait(false);
                                }
                            }
                        }
                        else
                        {
                            // Close the session and release the session lock after draining all messages for the accepted session.
                            if (_isSessionsEnabled)
                            {
                                await receiver.CloseAsync().ConfigureAwait(false);
                            }
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        // Ignore as we are stopping the host
                    }
                    catch (Exception ex)
                    {
                        // Log another exception
                        _logger.LogError(ex, $"An unhandled exception occurred in the message batch receive loop");

                        if (_isSessionsEnabled && receiver != null)
                        {
                            // Attempt to close the session and release session lock to accept a new session on the next loop iteration
                            try
                            {
                                await receiver.CloseAsync().ConfigureAwait(false);
                            }
                            catch
                            {
                                // Best effort
                                receiver = null;
                            }
                        }
                    }
                }
            }, cancellationToken);
        }
        internal void StartMessageBatchReceiver(CancellationToken cancellationToken)
        {
            SessionClient    sessionClient = null;
            IMessageReceiver receiver      = null;

            if (_isSessionsEnabled)
            {
                sessionClient = _sessionClient.Value;
            }
            else
            {
                receiver = Receiver;
            }

            Task.Run(async() =>
            {
                while (true)
                {
                    try
                    {
                        if (!_started || cancellationToken.IsCancellationRequested)
                        {
                            _logger.LogInformation("Message processing has been stopped or cancelled");
                            return;
                        }

                        if (_isSessionsEnabled && (receiver == null || receiver.IsClosedOrClosing))
                        {
                            try
                            {
                                receiver = await sessionClient.AcceptMessageSessionAsync();
                                receiver.PrefetchCount = _serviceBusOptions.PrefetchCount;
                            }
                            catch (ServiceBusTimeoutException)
                            {
                                // it's expected if the entity is empty, try next time
                                continue;
                            }
                        }

                        IList <Message> messages = await receiver.ReceiveAsync(_serviceBusOptions.BatchOptions.MaxMessageCount, _serviceBusOptions.BatchOptions.OperationTimeout);

                        if (messages != null)
                        {
                            Message[] messagesArray      = messages.ToArray();
                            ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateBatch(messagesArray);
                            input.MessageReceiver        = receiver;

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

                            if (cancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            // Complete batch of messages only if the execution was successful
                            if (_serviceBusOptions.BatchOptions.AutoComplete && _started)
                            {
                                if (result.Succeeded)
                                {
                                    await receiver.CompleteAsync(messagesArray.Select(x => x.SystemProperties.LockToken));
                                }
                                else
                                {
                                    List <Task> abandonTasks = new List <Task>();
                                    foreach (var lockTocken in messagesArray.Select(x => x.SystemProperties.LockToken))
                                    {
                                        abandonTasks.Add(receiver.AbandonAsync(lockTocken));
                                    }
                                    await Task.WhenAll(abandonTasks);
                                }
                            }
                        }
                        else
                        {
                            // Close the session and release the session lock after draining all messages for the accepted session.
                            if (_isSessionsEnabled)
                            {
                                await receiver.CloseAsync();
                            }
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        // Ignore as we are stopping the host
                    }
                    catch (Exception ex)
                    {
                        // Log another exception
                        _logger.LogError($"An unhandled exception occurred in the message batch receive loop", ex);

                        if (_isSessionsEnabled && receiver != null)
                        {
                            // Attempt to close the session and release session lock to accept a new session on the next loop iteration
                            try
                            {
                                await receiver.CloseAsync();
                            }
                            catch
                            {
                                // Best effort
                                receiver = null;
                            }
                        }
                    }
                }
            }, cancellationToken);
        }
Example #14
0
        internal void StartMessageBatchReceiver(CancellationToken cancellationToken)
        {
            SessionClient    sessionClient = null;
            IMessageReceiver receiver      = null;

            if (_isSessionsEnabled)
            {
                sessionClient = _messagingProvider.CreateSessionClient(_entityPath, _serviceBusAccount.ConnectionString);
            }
            else
            {
                receiver = Receiver;
            }

            Task.Run(async() =>
            {
                while (true)
                {
                    try
                    {
                        if (!_started || cancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        if (_isSessionsEnabled)
                        {
                            try
                            {
                                receiver = await sessionClient.AcceptMessageSessionAsync();
                            }
                            catch (ServiceBusTimeoutException)
                            {
                                // it's expected if the entity is empty, try next time
                                continue;
                            }
                        }

                        IList <Message> messages = await receiver.ReceiveAsync(_serviceBusOptions.BatchOptions.MaxMessageCount, _serviceBusOptions.BatchOptions.OperationTimeout);

                        if (messages != null)
                        {
                            Message[] messagesArray      = messages.ToArray();
                            ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateBatch(messagesArray);
                            input.MessageReceiver        = receiver;

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

                            if (cancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            // Complete batch of messages only if the execution was successful
                            if (_serviceBusOptions.BatchOptions.AutoComplete && _started)
                            {
                                if (result.Succeeded)
                                {
                                    await receiver.CompleteAsync(messagesArray.Select(x => x.SystemProperties.LockToken));
                                }
                                else
                                {
                                    // Delivery count is not incremented if
                                    // Session is accepted, the messages within the session are not completed (even if they are locked), and the session is closed
                                    // https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-sessions#impact-of-delivery-count
                                    if (_isSessionsEnabled)
                                    {
                                        List <Task> abandonTasks = new List <Task>();
                                        foreach (var lockTocken in messagesArray.Select(x => x.SystemProperties.LockToken))
                                        {
                                            abandonTasks.Add(receiver.AbandonAsync(lockTocken));
                                        }
                                        await Task.WhenAll(abandonTasks);
                                    }
                                }
                            }

                            // Close the session and release the session lock
                            if (_isSessionsEnabled)
                            {
                                await receiver.CloseAsync();
                            }
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        // Ignore as we are stopping the host
                    }
                    catch (Exception ex)
                    {
                        // Log another exception
                        _logger.LogError($"An unhandled exception occurred in the message batch receive loop: {ex.ToString()}");
                    }
                }
            }, cancellationToken);
        }
Example #15
0
        private async Task RunBatchReceiveLoopAsync(CancellationToken cancellationToken)
        {
            ServiceBusClient   sessionClient = null;
            ServiceBusReceiver receiver      = null;

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

            while (true)
            {
                try
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        _logger.LogInformation("Message processing has been stopped or cancelled");
                        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)
                    {
                        ServiceBusReceivedMessage[] messagesArray = messages.ToArray();
                        ServiceBusTriggerInput      input         = ServiceBusTriggerInput.CreateBatch(messagesArray);
                        if (_isSessionsEnabled)
                        {
                            input.MessageActions = new ServiceBusSessionMessageActions((ServiceBusSessionReceiver)receiver);
                        }
                        else
                        {
                            input.MessageActions = new ServiceBusMessageActions(receiver);
                        }
                        FunctionResult result = await _triggerExecutor.TryExecuteAsync(input.GetTriggerFunctionData(), cancellationToken).ConfigureAwait(false);

                        // Complete batch of messages only if the execution was successful
                        if (_autoCompleteMessagesOptionEvaluatedValue)
                        {
                            if (result.Succeeded)
                            {
                                List <Task> completeTasks = new List <Task>();
                                foreach (ServiceBusReceivedMessage message in messagesArray)
                                {
                                    // skip messages that were settled in the user's function
                                    if (input.MessageActions.SettledMessages.Contains(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
                            {
                                List <Task> abandonTasks = new List <Task>();
                                foreach (ServiceBusReceivedMessage message in messagesArray)
                                {
                                    // skip messages that were settled in the user's function
                                    if (input.MessageActions.SettledMessages.Contains(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);
                            }
                        }
                    }
                    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");
                    }
                catch (Exception ex)
                {
                    // Log another exception
                    _logger.LogError(ex, $"An unhandled exception occurred in the message batch receive loop");

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