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