Пример #1
0
        public async Task RoundRobinSessions()
        {
            await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: true))
            {
                await using var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString);
                ServiceBusSender sender = client.CreateSender(scope.QueueName);

                var messageCt             = 10;
                HashSet <string> sessions = new HashSet <string>()
                {
                    "1", "2", "3"
                };
                // send the messages
                foreach (string session in sessions)
                {
                    using ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync();

                    ServiceBusMessageBatch messageBatch = ServiceBusTestUtilities.AddMessages(batch, messageCt, session);
                    await sender.SendMessagesAsync(messageBatch);
                }

                // create receiver not scoped to a specific session
                for (int i = 0; i < 10; i++)
                {
                    ServiceBusSessionReceiver receiver = await client.AcceptNextSessionAsync(scope.QueueName);

                    foreach (ServiceBusReceivedMessage peekedMessage in await receiver.PeekMessagesAsync(
                                 fromSequenceNumber: 1,
                                 maxMessages: 10))
                    {
                        var sessionId = receiver.SessionId;
                        Assert.AreEqual(sessionId, peekedMessage.SessionId);
                    }

                    // Close the receiver client when we are done with it. Since the sessionClient doesn't own the underlying connection, the connection remains open, but the session link will be closed.
                    await receiver.DisposeAsync();
                }
            }
        }
Пример #2
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;
                        }
                    }
                }
            }
        }
        public async Task CreateFromReceivedMessageCopiesProperties()
        {
            await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: true, enableSession: true))
            {
                var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString);
                var sender = client.CreateSender(scope.QueueName);
                var msg    = new ServiceBusMessage(new BinaryData(GetRandomBuffer(100)));
                msg.ContentType   = "contenttype";
                msg.CorrelationId = "correlationid";
                msg.Subject       = "label";
                msg.MessageId     = "messageId";
                msg.PartitionKey  = "key";
                msg.ApplicationProperties.Add("testProp", "my prop");
                msg.ReplyTo              = "replyto";
                msg.ReplyToSessionId     = "replytosession";
                msg.ScheduledEnqueueTime = DateTimeOffset.Now;
                msg.SessionId            = "key";
                msg.TimeToLive           = TimeSpan.FromSeconds(60);
                msg.To = "to";
                await sender.SendMessageAsync(msg);

                ServiceBusSessionReceiver receiver = await client.AcceptNextSessionAsync(scope.QueueName);

                ServiceBusReceivedMessage received = await receiver.ReceiveMessageAsync();

                AmqpAnnotatedMessage rawReceived = received.GetRawAmqpMessage();
                Assert.IsNotNull(rawReceived.Header.DeliveryCount);
                Assert.IsTrue(rawReceived.MessageAnnotations.ContainsKey(AmqpMessageConstants.LockedUntilName));
                Assert.IsTrue(rawReceived.MessageAnnotations.ContainsKey(AmqpMessageConstants.SequenceNumberName));
                Assert.IsTrue(rawReceived.MessageAnnotations.ContainsKey(AmqpMessageConstants.EnqueueSequenceNumberName));
                Assert.IsTrue(rawReceived.MessageAnnotations.ContainsKey(AmqpMessageConstants.EnqueuedTimeUtcName));

                AssertMessagesEqual(msg, received);
                var toSend = new ServiceBusMessage(received);
                AmqpAnnotatedMessage rawSend = toSend.GetRawAmqpMessage();

                // verify that all system set properties have been cleared out
                Assert.IsNull(rawSend.Header.DeliveryCount);
                Assert.IsFalse(rawSend.MessageAnnotations.ContainsKey(AmqpMessageConstants.LockedUntilName));
                Assert.IsFalse(rawSend.MessageAnnotations.ContainsKey(AmqpMessageConstants.SequenceNumberName));
                Assert.IsFalse(rawSend.MessageAnnotations.ContainsKey(AmqpMessageConstants.DeadLetterSourceName));
                Assert.IsFalse(rawSend.MessageAnnotations.ContainsKey(AmqpMessageConstants.EnqueueSequenceNumberName));
                Assert.IsFalse(rawSend.MessageAnnotations.ContainsKey(AmqpMessageConstants.EnqueuedTimeUtcName));
                Assert.IsFalse(rawSend.MessageAnnotations.ContainsKey(AmqpMessageConstants.DeadLetterSourceName));
                Assert.IsFalse(toSend.ApplicationProperties.ContainsKey(AmqpMessageConstants.DeadLetterReasonHeader));
                Assert.IsFalse(toSend.ApplicationProperties.ContainsKey(AmqpMessageConstants.DeadLetterErrorDescriptionHeader));

                AssertMessagesEqual(toSend, received);

                void AssertMessagesEqual(ServiceBusMessage sentMessage, ServiceBusReceivedMessage received)
                {
                    Assert.IsTrue(received.Body.ToArray().SequenceEqual(sentMessage.Body.ToArray()));
                    Assert.AreEqual(received.ContentType, sentMessage.ContentType);
                    Assert.AreEqual(received.CorrelationId, sentMessage.CorrelationId);
                    Assert.AreEqual(received.Subject, sentMessage.Subject);
                    Assert.AreEqual(received.ContentType, sentMessage.ContentType);
                    Assert.AreEqual(received.CorrelationId, sentMessage.CorrelationId);
                    Assert.AreEqual(received.MessageId, sentMessage.MessageId);
                    Assert.AreEqual(received.PartitionKey, sentMessage.PartitionKey);
                    Assert.AreEqual((string)received.ApplicationProperties["testProp"], (string)sentMessage.ApplicationProperties["testProp"]);
                    Assert.AreEqual(received.ReplyTo, sentMessage.ReplyTo);
                    Assert.AreEqual(received.ReplyToSessionId, sentMessage.ReplyToSessionId);
                    Assert.AreEqual(received.ScheduledEnqueueTime.UtcDateTime.Second, sentMessage.ScheduledEnqueueTime.UtcDateTime.Second);
                    Assert.AreEqual(received.SessionId, sentMessage.SessionId);
                    Assert.AreEqual(received.TimeToLive, sentMessage.TimeToLive);
                    Assert.AreEqual(received.To, sentMessage.To);
                    Assert.AreEqual(received.TransactionPartitionKey, sentMessage.TransactionPartitionKey);
                }
            }
        }
Пример #4
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);
        }
        public async Task SenderReceiverActivities(bool useSessions)
        {
            await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: useSessions))
            {
                var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString);
                ServiceBusSender sender    = client.CreateSender(scope.QueueName);
                string           sessionId = null;
                if (useSessions)
                {
                    sessionId = "sessionId";
                }
                int numMessages = 5;
                var msgs        = ServiceBusTestUtilities.GetMessages(numMessages, sessionId);
                await sender.SendMessagesAsync(msgs);

                Activity[] sendActivities = AssertSendActivities(useSessions, sender, msgs);

                ServiceBusReceiver receiver = null;
                if (useSessions)
                {
                    receiver = await client.AcceptNextSessionAsync(scope.QueueName);
                }
                else
                {
                    receiver = client.CreateReceiver(scope.QueueName);
                }

                var remaining = numMessages;
                List <ServiceBusReceivedMessage> receivedMsgs = new List <ServiceBusReceivedMessage>();
                while (remaining > 0)
                {
                    // loop in case we don't receive all messages in one attempt
                    var received = await receiver.ReceiveMessagesAsync(remaining);

                    receivedMsgs.AddRange(received);
                    var receiveScope = _listener.AssertAndRemoveScope(DiagnosticProperty.ReceiveActivityName);
                    AssertCommonTags(receiveScope.Activity, receiver.EntityPath, receiver.FullyQualifiedNamespace);

                    var receiveLinkedActivities = receiveScope.LinkedActivities;
                    for (int i = 0; i < receiveLinkedActivities.Count; i++)
                    {
                        Assert.AreEqual(sendActivities[i].ParentId, receiveLinkedActivities[i].ParentId);
                    }
                    remaining -= received.Count;
                }

                var msgIndex = 0;

                var completed = receivedMsgs[msgIndex];
                await receiver.CompleteMessageAsync(completed);

                var completeScope = _listener.AssertAndRemoveScope(DiagnosticProperty.CompleteActivityName);
                AssertCommonTags(completeScope.Activity, receiver.EntityPath, receiver.FullyQualifiedNamespace);

                var deferred = receivedMsgs[++msgIndex];
                await receiver.DeferMessageAsync(deferred);

                var deferredScope = _listener.AssertAndRemoveScope(DiagnosticProperty.DeferActivityName);
                AssertCommonTags(deferredScope.Activity, receiver.EntityPath, receiver.FullyQualifiedNamespace);

                var deadLettered = receivedMsgs[++msgIndex];
                await receiver.DeadLetterMessageAsync(deadLettered);

                var deadLetterScope = _listener.AssertAndRemoveScope(DiagnosticProperty.DeadLetterActivityName);
                AssertCommonTags(deadLetterScope.Activity, receiver.EntityPath, receiver.FullyQualifiedNamespace);

                var abandoned = receivedMsgs[++msgIndex];
                await receiver.AbandonMessageAsync(abandoned);

                var abandonScope = _listener.AssertAndRemoveScope(DiagnosticProperty.AbandonActivityName);
                AssertCommonTags(abandonScope.Activity, receiver.EntityPath, receiver.FullyQualifiedNamespace);

                var receiveDeferMsg = await receiver.ReceiveDeferredMessageAsync(deferred.SequenceNumber);

                var receiveDeferScope = _listener.AssertAndRemoveScope(DiagnosticProperty.ReceiveDeferredActivityName);
                AssertCommonTags(receiveDeferScope.Activity, receiver.EntityPath, receiver.FullyQualifiedNamespace);

                // renew lock
                if (useSessions)
                {
                    var sessionReceiver = (ServiceBusSessionReceiver)receiver;
                    await sessionReceiver.RenewSessionLockAsync();

                    var renewSessionScope = _listener.AssertAndRemoveScope(DiagnosticProperty.RenewSessionLockActivityName);
                    AssertCommonTags(renewSessionScope.Activity, receiver.EntityPath, receiver.FullyQualifiedNamespace);

                    // set state
                    var state = new BinaryData("state");
                    await sessionReceiver.SetSessionStateAsync(state);

                    var setStateScope = _listener.AssertAndRemoveScope(DiagnosticProperty.SetSessionStateActivityName);
                    AssertCommonTags(setStateScope.Activity, sessionReceiver.EntityPath, sessionReceiver.FullyQualifiedNamespace);

                    // get state
                    var getState = await sessionReceiver.GetSessionStateAsync();

                    Assert.AreEqual(state.ToArray(), getState.ToArray());
                    var getStateScope = _listener.AssertAndRemoveScope(DiagnosticProperty.GetSessionStateActivityName);
                    AssertCommonTags(getStateScope.Activity, sessionReceiver.EntityPath, sessionReceiver.FullyQualifiedNamespace);
                }
                else
                {
                    await receiver.RenewMessageLockAsync(receivedMsgs[4]);

                    var renewMessageScope = _listener.AssertAndRemoveScope(DiagnosticProperty.RenewMessageLockActivityName);
                    AssertCommonTags(renewMessageScope.Activity, receiver.EntityPath, receiver.FullyQualifiedNamespace);
                }

                // schedule
                msgs = ServiceBusTestUtilities.GetMessages(numMessages, sessionId);

                foreach (var msg in msgs)
                {
                    var seq = await sender.ScheduleMessageAsync(msg, DateTimeOffset.UtcNow.AddMinutes(1));

                    Assert.IsNotNull(msg.ApplicationProperties[DiagnosticProperty.DiagnosticIdAttribute]);

                    var messageScope = _listener.AssertAndRemoveScope(DiagnosticProperty.MessageActivityName);
                    AssertCommonTags(messageScope.Activity, sender.EntityPath, sender.FullyQualifiedNamespace);

                    var scheduleScope = _listener.AssertAndRemoveScope(DiagnosticProperty.ScheduleActivityName);
                    AssertCommonTags(scheduleScope.Activity, sender.EntityPath, sender.FullyQualifiedNamespace);

                    var linkedActivities = scheduleScope.LinkedActivities;
                    Assert.AreEqual(1, linkedActivities.Count);
                    Assert.AreEqual(messageScope.Activity.Id, linkedActivities[0].ParentId);

                    await sender.CancelScheduledMessageAsync(seq);

                    var cancelScope = _listener.AssertAndRemoveScope(DiagnosticProperty.CancelActivityName);
                    AssertCommonTags(cancelScope.Activity, sender.EntityPath, sender.FullyQualifiedNamespace);
                }

                // send a batch
                var batch = await sender.CreateMessageBatchAsync();

                for (int i = 0; i < numMessages; i++)
                {
                    batch.TryAddMessage(ServiceBusTestUtilities.GetMessage(sessionId));
                }
                await sender.SendMessagesAsync(batch);

                AssertSendActivities(useSessions, sender, batch.AsReadOnly <ServiceBusMessage>());
            };
        }
Пример #6
0
        public async Task CanRoundTripAmqpProperties(bool enableSession)
        {
            await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: enableSession))
            {
                var message     = new ServiceBusMessage();
                var amqpMessage = message.GetRawAmqpMessage();

                // body
                amqpMessage.Body = AmqpMessageBody.FromValue("body");

                // header
                amqpMessage.Header.TimeToLive    = TimeSpan.FromSeconds(50);
                amqpMessage.Header.DeliveryCount = 3;
                amqpMessage.Header.Durable       = true;
                amqpMessage.Header.FirstAcquirer = true;
                amqpMessage.Header.Priority      = 1;

                // footer
                amqpMessage.Footer.Add("footerKey1", "footerVal1");
                amqpMessage.Footer.Add("footerKey2", "footerVal2");

                // properties
                amqpMessage.Properties.AbsoluteExpiryTime = DateTimeOffset.Now.AddDays(1);
                amqpMessage.Properties.ContentEncoding    = "compress";
                amqpMessage.Properties.ContentType        = "application/json";
                amqpMessage.Properties.CorrelationId      = new AmqpMessageId("correlationId");
                amqpMessage.Properties.CreationTime       = DateTimeOffset.Now.AddDays(1);
                amqpMessage.Properties.GroupId            = "groupId";
                amqpMessage.Properties.GroupSequence      = 5;
                amqpMessage.Properties.MessageId          = new AmqpMessageId("messageId");
                amqpMessage.Properties.ReplyTo            = new AmqpAddress("replyTo");
                amqpMessage.Properties.ReplyToGroupId     = "replyToGroupId";
                amqpMessage.Properties.Subject            = "subject";
                amqpMessage.Properties.To     = new AmqpAddress("to");
                amqpMessage.Properties.UserId = new byte[] { 1, 2, 3 };

                // application properties
                amqpMessage.ApplicationProperties.Add("applicationKey1", "applicationVal1");
                amqpMessage.ApplicationProperties.Add("applicationKey2", "applicationVal2");

                // message annotations
                amqpMessage.MessageAnnotations.Add("messageAnnotationKey1", "messageAnnotationVal1");
                amqpMessage.MessageAnnotations.Add("messageAnnotationKey2", "messageAnnotationVal2");

                // delivery annotations
                amqpMessage.DeliveryAnnotations.Add("deliveryAnnotationKey1", "deliveryAnnotationVal1");
                amqpMessage.DeliveryAnnotations.Add("deliveryAnnotationKey2", "deliveryAnnotationVal2");

                var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString);
                var sender = client.CreateSender(scope.QueueName);

                var now = DateTimeOffset.UtcNow;
                await sender.SendMessageAsync(message);

                var receiver = enableSession ? await client.AcceptNextSessionAsync(scope.QueueName)
                    : client.CreateReceiver(scope.QueueName);

                var received = (await receiver.ReceiveMessageAsync()).GetRawAmqpMessage();

                received.Body.TryGetValue(out var body);
                Assert.AreEqual("body", body);

                Assert.AreEqual(TimeSpan.FromSeconds(50), received.Header.TimeToLive);

                // the broker will disregard the value set for delivery count
                Assert.AreEqual(1, received.Header.DeliveryCount);
                Assert.IsTrue(received.Header.Durable);
                Assert.IsTrue(received.Header.FirstAcquirer);
                Assert.AreEqual(1, received.Header.Priority);

                Assert.AreEqual("compress", received.Properties.ContentEncoding);
                Assert.AreEqual("application/json", received.Properties.ContentType);
                Assert.AreEqual(new AmqpMessageId("correlationId"), received.Properties.CorrelationId);
                Assert.AreEqual("groupId", received.Properties.GroupId);
                Assert.AreEqual(5, received.Properties.GroupSequence);
                Assert.AreEqual(new AmqpMessageId("messageId"), received.Properties.MessageId);
                Assert.AreEqual(new AmqpAddress("replyTo"), received.Properties.ReplyTo);
                Assert.AreEqual("replyToGroupId", received.Properties.ReplyToGroupId);
                Assert.AreEqual("subject", received.Properties.Subject);
                Assert.AreEqual(new AmqpAddress("to"), received.Properties.To);
                Assert.AreEqual(new byte[] { 1, 2, 3 }, received.Properties.UserId.Value.ToArray());

                // since TTL was set these were overriden - provide some buffer since the Now time is
                Assert.That(received.Properties.CreationTime, Is.EqualTo(now).Within(TimeSpan.FromSeconds(1)));
                Assert.That(received.Properties.AbsoluteExpiryTime, Is.EqualTo(now.Add(TimeSpan.FromSeconds(50))).Within(TimeSpan.FromSeconds(1)));

                // application properties
                Assert.AreEqual(received.ApplicationProperties["applicationKey1"], "applicationVal1");
                Assert.AreEqual(received.ApplicationProperties["applicationKey2"], "applicationVal2");

                // message annotations
                Assert.AreEqual(received.MessageAnnotations["messageAnnotationKey1"], "messageAnnotationVal1");
                Assert.AreEqual(received.MessageAnnotations["messageAnnotationKey2"], "messageAnnotationVal2");

                // delivery annotations
                Assert.AreEqual(received.DeliveryAnnotations["deliveryAnnotationKey1"], "deliveryAnnotationVal1");
                Assert.AreEqual(received.DeliveryAnnotations["deliveryAnnotationKey2"], "deliveryAnnotationVal2");
            }
        }
        private async Task RunSessionReceiveLoopAsync()
        {
            var  cred         = new DefaultAzureCredential();
            var  client       = new ServiceBusClient(NamespaceName, cred);
            long messageCount = 0;
            var  sw           = Stopwatch.StartNew();
            var  bagStart     = sw.ElapsedMilliseconds;

            Console.WriteLine($"Receiving from entity '{EntityName}' in namespace '{NamespaceName}'");

            var timer = new Timer(state =>
            {
                var snapshot    = Interlocked.Exchange(ref messageCount, 0);
                var bagDuration = sw.ElapsedMilliseconds - bagStart;
                bagStart        = sw.ElapsedMilliseconds;

                Console.ResetColor();
                Console.WriteLine($"\nReceived {snapshot / (bagDuration / 1000.0)} msg/sec,  {snapshot} in {bagDuration} ms");
            }, null, 10000, 10000);


            CancellationTokenSource cts = new CancellationTokenSource();

            Console.CancelKeyPress += (a, o) => { cts.Cancel(); };


            do
            {
                var receiver = await client.AcceptNextSessionAsync(EntityName, new ServiceBusSessionReceiverOptions()
                {
                    PrefetchCount = this.PrefetchCount,
                    ReceiveMode   = this.ReceiveDelete ? ServiceBusReceiveMode.ReceiveAndDelete : ServiceBusReceiveMode.PeekLock,
                });

                ServiceBusReceivedMessage message = null;
                do
                {
                    message = await receiver.ReceiveMessageAsync(TimeSpan.FromSeconds(1), cancellationToken : cts.Token);

                    if (message != null)
                    {
                        try
                        {
                            if (!string.IsNullOrEmpty(message.SessionId) ||
                                !string.IsNullOrEmpty(message.Subject))
                            {
                                int color = Math.Abs(string.IsNullOrEmpty(message.SessionId) ? message.Subject.GetHashCode() : int.Parse(message.SessionId));
                                Console.BackgroundColor = ConsoleColor.Black;
                                Console.ForegroundColor = (ConsoleColor)((color % 14) + 1);
                            }
                            Console.Write("[]");
                            Interlocked.Increment(ref messageCount);
                            if (!this.ReceiveDelete)
                            {
                                await receiver.CompleteMessageAsync(message, cts.Token);
                            }
                        }
                        catch
                        {
                            if (!this.ReceiveDelete)
                            {
                                await receiver.AbandonMessageAsync(message, cancellationToken : cts.Token);
                            }
                        }
                    }
                }while (message != null && !cts.IsCancellationRequested);
                await receiver.CloseAsync();
            }while (!cts.IsCancellationRequested);

            timer.Dispose();
            await client.DisposeAsync();
        }
Пример #8
0
        public async Task SenderReceiverActivities(bool useSessions)
        {
            await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: useSessions))
            {
                var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString);
                ServiceBusSender sender    = client.CreateSender(scope.QueueName);
                string           sessionId = null;
                if (useSessions)
                {
                    sessionId = "sessionId";
                }
                int numMessages = 5;
                var msgs        = GetMessages(numMessages, sessionId);
                await sender.SendMessagesAsync(msgs);

                Activity[] sendActivities = AssertSendActivities(useSessions, sender, msgs);

                ServiceBusReceiver receiver = null;
                if (useSessions)
                {
                    receiver = await client.AcceptNextSessionAsync(scope.QueueName);
                }
                else
                {
                    receiver = client.CreateReceiver(scope.QueueName);
                }

                var remaining = numMessages;
                List <ServiceBusReceivedMessage> receivedMsgs = new List <ServiceBusReceivedMessage>();
                while (remaining > 0)
                {
                    // loop in case we don't receive all messages in one attempt
                    var received = await receiver.ReceiveMessagesAsync(remaining);

                    receivedMsgs.AddRange(received);
                    (string Key, object Value, DiagnosticListener)receiveStart = _listener.Events.Dequeue();
                    Assert.AreEqual(DiagnosticProperty.ReceiveActivityName + ".Start", receiveStart.Key);

                    Activity receiveActivity = (Activity)receiveStart.Value;
                    AssertCommonTags(receiveActivity, receiver.EntityPath, receiver.FullyQualifiedNamespace);
                    CollectionAssert.Contains(
                        receiveActivity.Tags,
                        new KeyValuePair <string, string>(
                            DiagnosticProperty.RequestedMessageCountAttribute,
                            remaining.ToString()));
                    CollectionAssert.Contains(
                        receiveActivity.Tags,
                        new KeyValuePair <string, string>(
                            DiagnosticProperty.MessageIdAttribute,
                            string.Join(",", received.Select(m => m.MessageId).ToArray())));
                    remaining -= received.Count;

                    if (useSessions)
                    {
                        CollectionAssert.Contains(
                            receiveActivity.Tags,
                            new KeyValuePair <string, string>(
                                DiagnosticProperty.SessionIdAttribute,
                                string.Join(",", msgs.Select(m => m.SessionId).Distinct().ToArray())));
                    }
                    var receiveLinkedActivities = ((IEnumerable <Activity>)receiveActivity.GetType().GetProperty("Links").GetValue(receiveActivity)).ToArray();
                    for (int i = 0; i < receiveLinkedActivities.Length; i++)
                    {
                        Assert.AreEqual(sendActivities[i].ParentId, receiveLinkedActivities[i].ParentId);
                    }
                    (string Key, object Value, DiagnosticListener)receiveStop = _listener.Events.Dequeue();

                    Assert.AreEqual(DiagnosticProperty.ReceiveActivityName + ".Stop", receiveStop.Key);
                }

                var msgIndex = 0;

                var completed = receivedMsgs[msgIndex];
                await receiver.CompleteMessageAsync(completed);

                (string Key, object Value, DiagnosticListener)completeStart = _listener.Events.Dequeue();
                Assert.AreEqual(DiagnosticProperty.CompleteActivityName + ".Start", completeStart.Key);
                Activity completeActivity = (Activity)completeStart.Value;
                AssertCommonTags(completeActivity, receiver.EntityPath, receiver.FullyQualifiedNamespace);
                AssertLockTokensTag(completeActivity, completed.LockToken);
                (string Key, object Value, DiagnosticListener)completeStop = _listener.Events.Dequeue();
                Assert.AreEqual(DiagnosticProperty.CompleteActivityName + ".Stop", completeStop.Key);

                var deferred = receivedMsgs[++msgIndex];
                await receiver.DeferMessageAsync(deferred);

                (string Key, object Value, DiagnosticListener)deferStart = _listener.Events.Dequeue();
                Assert.AreEqual(DiagnosticProperty.DeferActivityName + ".Start", deferStart.Key);
                Activity deferActivity = (Activity)deferStart.Value;
                AssertCommonTags(deferActivity, receiver.EntityPath, receiver.FullyQualifiedNamespace);
                AssertLockTokensTag(deferActivity, deferred.LockToken);
                (string Key, object Value, DiagnosticListener)deferStop = _listener.Events.Dequeue();
                Assert.AreEqual(DiagnosticProperty.DeferActivityName + ".Stop", deferStop.Key);

                var deadLettered = receivedMsgs[++msgIndex];
                await receiver.DeadLetterMessageAsync(deadLettered);

                (string Key, object Value, DiagnosticListener)deadLetterStart = _listener.Events.Dequeue();
                Assert.AreEqual(DiagnosticProperty.DeadLetterActivityName + ".Start", deadLetterStart.Key);
                Activity deadLetterActivity = (Activity)deadLetterStart.Value;
                AssertCommonTags(deadLetterActivity, receiver.EntityPath, receiver.FullyQualifiedNamespace);
                AssertLockTokensTag(deadLetterActivity, deadLettered.LockToken);
                (string Key, object Value, DiagnosticListener)deadletterStop = _listener.Events.Dequeue();
                Assert.AreEqual(DiagnosticProperty.DeadLetterActivityName + ".Stop", deadletterStop.Key);

                var abandoned = receivedMsgs[++msgIndex];
                await receiver.AbandonMessageAsync(abandoned);

                (string Key, object Value, DiagnosticListener)abandonStart = _listener.Events.Dequeue();
                Assert.AreEqual(DiagnosticProperty.AbandonActivityName + ".Start", abandonStart.Key);
                Activity abandonActivity = (Activity)abandonStart.Value;
                AssertCommonTags(abandonActivity, receiver.EntityPath, receiver.FullyQualifiedNamespace);
                AssertLockTokensTag(abandonActivity, abandoned.LockToken);
                (string Key, object Value, DiagnosticListener)abandonStop = _listener.Events.Dequeue();
                Assert.AreEqual(DiagnosticProperty.AbandonActivityName + ".Stop", abandonStop.Key);

                var receiveDeferMsg = await receiver.ReceiveDeferredMessageAsync(deferred.SequenceNumber);

                (string Key, object Value, DiagnosticListener)receiveDeferStart = _listener.Events.Dequeue();
                Assert.AreEqual(DiagnosticProperty.ReceiveDeferredActivityName + ".Start", receiveDeferStart.Key);
                Activity receiveDeferActivity = (Activity)receiveDeferStart.Value;
                AssertCommonTags(receiveDeferActivity, receiver.EntityPath, receiver.FullyQualifiedNamespace);
                CollectionAssert.Contains(
                    receiveDeferActivity.Tags,
                    new KeyValuePair <string, string>(
                        DiagnosticProperty.MessageIdAttribute,
                        deferred.MessageId));
                CollectionAssert.Contains(
                    receiveDeferActivity.Tags,
                    new KeyValuePair <string, string>(
                        DiagnosticProperty.SequenceNumbersAttribute,
                        deferred.SequenceNumber.ToString()));
                (string Key, object Value, DiagnosticListener)receiveDeferStop = _listener.Events.Dequeue();
                Assert.AreEqual(DiagnosticProperty.ReceiveDeferredActivityName + ".Stop", receiveDeferStop.Key);

                // renew lock
                if (useSessions)
                {
                    var sessionReceiver = (ServiceBusSessionReceiver)receiver;
                    await sessionReceiver.RenewSessionLockAsync();

                    (string Key, object Value, DiagnosticListener)renewStart = _listener.Events.Dequeue();
                    Assert.AreEqual(DiagnosticProperty.RenewSessionLockActivityName + ".Start", renewStart.Key);
                    Activity renewActivity = (Activity)renewStart.Value;
                    AssertCommonTags(renewActivity, receiver.EntityPath, receiver.FullyQualifiedNamespace);
                    CollectionAssert.Contains(
                        renewActivity.Tags,
                        new KeyValuePair <string, string>(
                            DiagnosticProperty.SessionIdAttribute,
                            "sessionId"));

                    (string Key, object Value, DiagnosticListener)renewStop = _listener.Events.Dequeue();
                    Assert.AreEqual(DiagnosticProperty.RenewSessionLockActivityName + ".Stop", renewStop.Key);

                    // set state
                    var state = Encoding.UTF8.GetBytes("state");
                    await sessionReceiver.SetSessionStateAsync(state);

                    (string Key, object Value, DiagnosticListener)setStateStart = _listener.Events.Dequeue();
                    Assert.AreEqual(DiagnosticProperty.SetSessionStateActivityName + ".Start", setStateStart.Key);
                    Activity setStateActivity = (Activity)setStateStart.Value;
                    AssertCommonTags(setStateActivity, sessionReceiver.EntityPath, sessionReceiver.FullyQualifiedNamespace);
                    CollectionAssert.Contains(
                        setStateActivity.Tags,
                        new KeyValuePair <string, string>(
                            DiagnosticProperty.SessionIdAttribute,
                            "sessionId"));

                    (string Key, object Value, DiagnosticListener)setStateStop = _listener.Events.Dequeue();
                    Assert.AreEqual(DiagnosticProperty.SetSessionStateActivityName + ".Stop", setStateStop.Key);

                    // get state
                    var getState = await sessionReceiver.GetSessionStateAsync();

                    Assert.AreEqual(state, getState);
                    (string Key, object Value, DiagnosticListener)getStateStart = _listener.Events.Dequeue();
                    Assert.AreEqual(DiagnosticProperty.GetSessionStateActivityName + ".Start", getStateStart.Key);
                    Activity getStateActivity = (Activity)getStateStart.Value;
                    AssertCommonTags(getStateActivity, sessionReceiver.EntityPath, sessionReceiver.FullyQualifiedNamespace);
                    CollectionAssert.Contains(
                        getStateActivity.Tags,
                        new KeyValuePair <string, string>(
                            DiagnosticProperty.SessionIdAttribute,
                            "sessionId"));

                    (string Key, object Value, DiagnosticListener)getStateStop = _listener.Events.Dequeue();
                    Assert.AreEqual(DiagnosticProperty.GetSessionStateActivityName + ".Stop", getStateStop.Key);
                }
                else
                {
                    await receiver.RenewMessageLockAsync(receivedMsgs[4]);

                    (string Key, object Value, DiagnosticListener)renewStart = _listener.Events.Dequeue();
                    Assert.AreEqual(DiagnosticProperty.RenewMessageLockActivityName + ".Start", renewStart.Key);
                    Activity renewActivity = (Activity)renewStart.Value;
                    AssertCommonTags(renewActivity, receiver.EntityPath, receiver.FullyQualifiedNamespace);
                    AssertLockTokensTag(renewActivity, receivedMsgs[4].LockToken);
                    CollectionAssert.Contains(
                        renewActivity.Tags,
                        new KeyValuePair <string, string>(
                            DiagnosticProperty.LockedUntilAttribute,
                            receivedMsgs[4].LockedUntil.ToString()));

                    (string Key, object Value, DiagnosticListener)renewStop = _listener.Events.Dequeue();
                    Assert.AreEqual(DiagnosticProperty.RenewMessageLockActivityName + ".Stop", renewStop.Key);
                }

                // schedule
                msgs = GetMessages(numMessages, sessionId);

                foreach (var msg in msgs)
                {
                    var seq = await sender.ScheduleMessageAsync(msg, DateTimeOffset.UtcNow.AddMinutes(1));

                    Assert.IsNotNull(msg.ApplicationProperties[DiagnosticProperty.DiagnosticIdAttribute]);

                    (string Key, object Value, DiagnosticListener)startMessage = _listener.Events.Dequeue();
                    Activity messageActivity = (Activity)startMessage.Value;
                    AssertCommonTags(messageActivity, sender.EntityPath, sender.FullyQualifiedNamespace);
                    Assert.AreEqual(DiagnosticProperty.MessageActivityName + ".Start", startMessage.Key);

                    (string Key, object Value, DiagnosticListener)stopMessage = _listener.Events.Dequeue();
                    Assert.AreEqual(DiagnosticProperty.MessageActivityName + ".Stop", stopMessage.Key);

                    (string Key, object Value, DiagnosticListener)startSchedule = _listener.Events.Dequeue();
                    AssertCommonTags((Activity)startSchedule.Value, sender.EntityPath, sender.FullyQualifiedNamespace);

                    Assert.AreEqual(DiagnosticProperty.ScheduleActivityName + ".Start", startSchedule.Key);
                    (string Key, object Value, DiagnosticListener)stopSchedule = _listener.Events.Dequeue();

                    Assert.AreEqual(DiagnosticProperty.ScheduleActivityName + ".Stop", stopSchedule.Key);
                    var linkedActivities = ((IEnumerable <Activity>)startSchedule.Value.GetType().GetProperty("Links").GetValue(startSchedule.Value)).ToArray();
                    Assert.AreEqual(1, linkedActivities.Length);
                    Assert.AreEqual(messageActivity.Id, linkedActivities[0].ParentId);

                    await sender.CancelScheduledMessageAsync(seq);

                    (string Key, object Value, DiagnosticListener)startCancel = _listener.Events.Dequeue();
                    AssertCommonTags((Activity)startCancel.Value, sender.EntityPath, sender.FullyQualifiedNamespace);
                    Assert.AreEqual(DiagnosticProperty.CancelActivityName + ".Start", startCancel.Key);

                    (string Key, object Value, DiagnosticListener)stopCancel = _listener.Events.Dequeue();
                    Assert.AreEqual(DiagnosticProperty.CancelActivityName + ".Stop", stopCancel.Key);
                }

                // send a batch
                var batch = await sender.CreateMessageBatchAsync();

                for (int i = 0; i < numMessages; i++)
                {
                    batch.TryAddMessage(GetMessage(sessionId));
                }
                await sender.SendMessagesAsync(batch);

                AssertSendActivities(useSessions, sender, batch.AsEnumerable <ServiceBusMessage>());
            };
        }
Пример #9
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;
                        }
                    }
                }
            }
        }