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(ServiceBusTestUtilities.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.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(rawSend.MessageAnnotations.ContainsKey(AmqpMessageConstants.MessageStateName)); 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); } } }
public async Task CrossEntityTransactionSendsFirstRollback() { await using var client = CreateCrossEntityTxnClient(); await using var queueA = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : false); await using var queueB = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : false); await using var queueC = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : false); await using var noTxClient = CreateClient(); var senderA = noTxClient.CreateSender(queueA.QueueName); var receiverA = client.CreateReceiver(queueA.QueueName); var receiverB = client.CreateReceiver(queueB.QueueName); var senderB = client.CreateSender(queueB.QueueName); var senderC = client.CreateSender(queueC.QueueName); var receiverC = noTxClient.CreateReceiver(queueC.QueueName); var message = new ServiceBusMessage(); // B is the send via entity since it is first await senderB.SendMessageAsync(message); await senderA.SendMessageAsync(message); // you can't use a receiver after a sender (for a different entity) when using a Transaction Group because it would be // saying that you want to receive via the sender entity which isn't possible Assert.ThrowsAsync <InvalidOperationException>(async() => await receiverA.ReceiveMessageAsync()); // After the above throws, the session gets closed by the AMQP lib, so we are testing whether the fault tolerant session/controller // objects get re-created correctly. ServiceBusReceivedMessage receivedMessageB = await receiverB.ReceiveMessageAsync(); // If the transaction succeeds, then all the operations occurred on the same partition. var transaction = new CommittableTransaction(); using (var ts = new TransactionScope(transaction, TransactionScopeAsyncFlowOption.Enabled)) { // this is allowed because it is on B await receiverB.CompleteMessageAsync(receivedMessageB); // send to C via B - this is allowed because we are sending await senderC.SendMessageAsync(message); ts.Complete(); } transaction.Rollback(); // Adding delay since transaction Commit/Rollback is an asynchronous operation. await Task.Delay(TimeSpan.FromSeconds(2)); await receiverB.AbandonMessageAsync(receivedMessageB); receivedMessageB = await receiverB.ReceiveMessageAsync(); Assert.IsNotNull(receivedMessageB); await receiverB.AbandonMessageAsync(receivedMessageB); var receivedMessageC = await receiverC.ReceiveMessageAsync(); Assert.IsNull(receivedMessageC); // If the transaction succeeds, then all the operations occurred on the same partition. using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { receivedMessageB = await receiverB.ReceiveMessageAsync(); // this is allowed because it is on B await receiverB.CompleteMessageAsync(receivedMessageB); // this will fail because it is not part of txn group Assert.ThrowsAsync <ServiceBusException>(async() => await senderA.SendMessageAsync(message)); ts.Complete(); } receivedMessageB = await receiverB.ReceiveMessageAsync(); Assert.IsNull(receivedMessageB); }
public async Task TransactionThrowsWhenOperationsOfDifferentPartitionsAreInSameTransaction() { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: true, enableSession: false)) { var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); ServiceBusSender sender = client.CreateSender(scope.QueueName); ServiceBusReceiver receiver = client.CreateReceiver(scope.QueueName); string body = Guid.NewGuid().ToString("N"); ServiceBusMessage message1 = GetMessage(partitionKey: "1"); ServiceBusMessage message2 = GetMessage(partitionKey: "2"); // Two send operations to different partitions. var transaction = new CommittableTransaction(); using (TransactionScope ts = new TransactionScope(transaction, TransactionScopeAsyncFlowOption.Enabled)) { await sender.SendAsync(message1); Assert.ThrowsAsync <InvalidOperationException>( async() => await sender.SendAsync(message2)); ts.Complete(); } transaction.Rollback(); // Adding delay since transaction Commit/Rollback is an asynchronous operation. // Operating on the same message should not be done. await Task.Delay(TimeSpan.FromSeconds(2)); // Two complete operations to different partitions. await sender.SendAsync(message1); await sender.SendAsync(message2); ServiceBusReceivedMessage receivedMessage1 = await receiver.ReceiveAsync(); Assert.NotNull(receivedMessage1); ServiceBusReceivedMessage receivedMessage2 = await receiver.ReceiveAsync(); Assert.NotNull(receivedMessage2); transaction = new CommittableTransaction(); using (TransactionScope ts = new TransactionScope(transaction, TransactionScopeAsyncFlowOption.Enabled)) { await receiver.CompleteAsync(receivedMessage1); Assert.ThrowsAsync <InvalidOperationException>( async() => await receiver.CompleteAsync(receivedMessage2)); ts.Complete(); } transaction.Rollback(); // Adding delay since transaction Commit/Rollback is an asynchronous operation. // Operating on the same message should not be done. await Task.Delay(TimeSpan.FromSeconds(2)); await receiver.CompleteAsync(receivedMessage1); // the service seems to abandon the message that // triggered the InvalidOperationException // in the transaction Assert.That( async() => await receiver.CompleteAsync(receivedMessage2), Throws.InstanceOf <ServiceBusException>() .And.Property(nameof(ServiceBusException.Reason)) .EqualTo(ServiceBusException.FailureReason.MessageLockLost)); } }
public async Task UserCallbackThrowingCausesMessageToBeAbandonedIfNotSettled(string settleMethod) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: false)) { await using var client = GetClient(); var sender = client.CreateSender(scope.QueueName); await sender.SendAsync(GetMessage()); var processor = client.CreateProcessor(scope.QueueName, new ServiceBusProcessorOptions { AutoComplete = true }); var tcs = new TaskCompletionSource <bool>(); async Task ProcessMessage(ProcessMessageEventArgs args) { switch (settleMethod) { case "Abandon": await args.AbandonAsync(args.Message); break; case "Complete": await args.CompleteAsync(args.Message); break; case "Defer": await args.DeferAsync(args.Message); break; case "Deadletter": await args.DeadLetterAsync(args.Message); break; case "DeadletterOverload": await args.DeadLetterAsync(args.Message, "reason"); break; } throw new TestException(); } Task ExceptionHandler(ProcessErrorEventArgs args) { tcs.SetResult(true); if (!(args.Exception is TestException)) { Assert.Fail(args.Exception.ToString()); } return(Task.CompletedTask); } processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(); await tcs.Task; await processor.StopProcessingAsync(); var receiver = client.CreateReceiver(scope.QueueName); var msg = await receiver.ReceiveAsync(TimeSpan.FromSeconds(5)); if (settleMethod == "" || settleMethod == "Abandon") { // if the message is abandoned (whether by user callback or // by processor due to call back throwing), the message will // be available to receive again immediately. Assert.IsNotNull(msg); } else { Assert.IsNull(msg); } } }
public async Task CrossEntityTransactionReceivesFirstRollback() { await using var client = CreateCrossEntityTxnClient(); await using var queueA = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : false); await using var queueB = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : false); await using var topicC = await ServiceBusScope.CreateWithTopic(enablePartitioning : false, enableSession : false); await using var noTxClient = CreateClient(); var senderA = noTxClient.CreateSender(queueA.QueueName); var receiverA = client.CreateReceiver(queueA.QueueName); var senderB = client.CreateSender(queueB.QueueName); var senderC = client.CreateSender(topicC.TopicName); var message = new ServiceBusMessage(); await senderA.SendMessageAsync(message); ServiceBusReceivedMessage receivedMessage = await receiverA.ReceiveMessageAsync(); using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { await receiverA.CompleteMessageAsync(receivedMessage); await senderB.SendMessageAsync(message); await senderC.SendMessageAsync(message); } await receiverA.AbandonMessageAsync(receivedMessage); // transaction wasn't committed - verify that it was rolled back receivedMessage = await receiverA.ReceiveMessageAsync(); Assert.IsNotNull(receivedMessage); await receiverA.AbandonMessageAsync(receivedMessage); var receiverB = noTxClient.CreateReceiver(queueB.QueueName); receivedMessage = await receiverB.ReceiveMessageAsync(TimeSpan.FromSeconds(10)); Assert.IsNull(receivedMessage); var receiverC = noTxClient.CreateReceiver(topicC.TopicName, topicC.SubscriptionNames.First()); receivedMessage = await receiverC.ReceiveMessageAsync(TimeSpan.FromSeconds(10)); Assert.IsNull(receivedMessage); receivedMessage = await receiverA.ReceiveMessageAsync(); // now commit the transaction using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { await receiverA.CompleteMessageAsync(receivedMessage); await senderB.SendMessageAsync(message); await senderC.SendMessageAsync(message); ts.Complete(); } receivedMessage = await receiverA.ReceiveMessageAsync(TimeSpan.FromSeconds(10)); Assert.IsNull(receivedMessage); receivedMessage = await receiverB.ReceiveMessageAsync(); Assert.IsNotNull(receivedMessage); receivedMessage = await receiverC.ReceiveMessageAsync(); Assert.IsNotNull(receivedMessage); }
public async Task LogsEvents() { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: false)) { await using var client = GetNoRetryClient(); _listener.SingleEventById(ServiceBusEventSource.ClientCreateStartEvent, e => e.Payload.Contains(nameof(ServiceBusClient)) && e.Payload.Contains(client.FullyQualifiedNamespace)); var messageCount = 10; ServiceBusSender sender = client.CreateSender(scope.QueueName); _listener.EventsById(ServiceBusEventSource.ClientCreateStartEvent).Where(e => e.Payload.Contains(nameof(ServiceBusSender)) && e.Payload.Contains(sender.FullyQualifiedNamespace) && e.Payload.Contains(sender.EntityPath)); using ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync(); _listener.SingleEventById(ServiceBusEventSource.CreateMessageBatchStartEvent, e => e.Payload.Contains(sender.Identifier)); _listener.SingleEventById(ServiceBusEventSource.CreateMessageBatchCompleteEvent, e => e.Payload.Contains(sender.Identifier)); IEnumerable <ServiceBusMessage> messages = AddMessages(batch, messageCount).AsEnumerable <ServiceBusMessage>(); await sender.SendMessagesAsync(batch); _listener.SingleEventById(ServiceBusEventSource.CreateSendLinkStartEvent, e => e.Payload.Contains(sender.Identifier)); _listener.SingleEventById(ServiceBusEventSource.CreateSendLinkCompleteEvent, e => e.Payload.Contains(sender.Identifier)); _listener.SingleEventById(ServiceBusEventSource.SendMessageStartEvent, e => e.Payload.Contains(sender.Identifier)); _listener.SingleEventById(ServiceBusEventSource.SendMessageCompleteEvent, e => e.Payload.Contains(sender.Identifier)); Assert.That( async() => await client.CreateSessionReceiverAsync(scope.QueueName), Throws.InstanceOf <InvalidOperationException>()); _listener.SingleEventById(ServiceBusEventSource.ClientCreateStartEvent, e => e.Payload.Contains(nameof(ServiceBusSessionReceiver)) && e.Payload.Contains(client.FullyQualifiedNamespace) && e.Payload.Contains(scope.QueueName)); _listener.SingleEventById(ServiceBusEventSource.ClientCreateExceptionEvent, e => e.Payload.Contains(nameof(ServiceBusSessionReceiver)) && e.Payload.Contains(client.FullyQualifiedNamespace) && e.Payload.Contains(scope.QueueName)); var receiver = client.CreateReceiver(scope.QueueName); _listener.SingleEventById(ServiceBusEventSource.ClientCreateStartEvent, e => e.Payload.Contains(nameof(ServiceBusReceiver)) && e.Payload.Contains(client.FullyQualifiedNamespace)); var messageEnum = messages.GetEnumerator(); var remainingMessages = messageCount; while (remainingMessages > 0) { foreach (var item in await receiver.ReceiveMessagesAsync(remainingMessages)) { remainingMessages--; messageEnum.MoveNext(); Assert.AreEqual(messageEnum.Current.MessageId, item.MessageId); Assert.AreEqual(item.DeliveryCount, 1); } } _listener.SingleEventById(ServiceBusEventSource.CreateReceiveLinkStartEvent, e => e.Payload.Contains(receiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.CreateReceiveLinkCompleteEvent, e => e.Payload.Contains(receiver.Identifier)); Assert.IsTrue(_listener.EventsById(ServiceBusEventSource.ReceiveMessageStartEvent).Any()); Assert.IsTrue(_listener.EventsById(ServiceBusEventSource.ReceiveMessageCompleteEvent).Any()); Assert.AreEqual(0, remainingMessages); messageEnum.Reset(); foreach (var item in await receiver.PeekMessagesAsync(messageCount)) { messageEnum.MoveNext(); Assert.AreEqual(messageEnum.Current.MessageId, item.MessageId); } _listener.SingleEventById(ServiceBusEventSource.CreateManagementLinkStartEvent, e => e.Payload.Contains(receiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.CreateManagementLinkCompleteEvent, e => e.Payload.Contains(receiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.PeekMessageStartEvent, e => e.Payload.Contains(receiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.PeekMessageCompleteEvent, e => e.Payload.Contains(receiver.Identifier)); var seq = await sender.ScheduleMessageAsync(new ServiceBusMessage(), DateTimeOffset.UtcNow.AddMinutes(1)); _listener.SingleEventById(ServiceBusEventSource.ScheduleMessageStartEvent, e => e.Payload.Contains(sender.Identifier)); _listener.SingleEventById(ServiceBusEventSource.ScheduleMessageCompleteEvent, e => e.Payload.Contains(sender.Identifier)); await sender.CancelScheduledMessageAsync(seq); _listener.SingleEventById(ServiceBusEventSource.CancelScheduledMessageStartEvent, e => e.Payload.Contains(sender.Identifier)); _listener.SingleEventById(ServiceBusEventSource.CancelScheduledMessageCompleteEvent, e => e.Payload.Contains(sender.Identifier)); await receiver.DisposeAsync(); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeStartEvent, e => e.Payload.Contains(nameof(ServiceBusReceiver)) && e.Payload.Contains(receiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeCompleteEvent, e => e.Payload.Contains(nameof(ServiceBusReceiver)) && e.Payload.Contains(receiver.Identifier)); // link closed event is fired asynchronously, so add a small delay await Task.Delay(TimeSpan.FromSeconds(5)); _listener.SingleEventById(ServiceBusEventSource.ReceiveLinkClosedEvent, e => e.Payload.Contains(receiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.ManagementLinkClosedEvent, e => e.Payload.Contains(receiver.Identifier)); Assert.IsFalse(_listener.EventsById(ServiceBusEventSource.MaxMessagesExceedsPrefetchEvent).Any()); receiver = client.CreateReceiver(scope.QueueName, new ServiceBusReceiverOptions { PrefetchCount = 10 }); await receiver.ReceiveMessagesAsync(20, TimeSpan.FromSeconds(1)); _listener.SingleEventById(ServiceBusEventSource.MaxMessagesExceedsPrefetchEvent, e => e.Payload.Contains(receiver.Identifier)); await sender.DisposeAsync(); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeStartEvent, e => e.Payload.Contains(nameof(ServiceBusSender)) && e.Payload.Contains(sender.Identifier)); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeCompleteEvent, e => e.Payload.Contains(nameof(ServiceBusSender)) && e.Payload.Contains(sender.Identifier)); await client.DisposeAsync(); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeStartEvent, e => e.Payload.Contains(nameof(ServiceBusClient)) && e.Payload.Contains(client.Identifier)); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeCompleteEvent, e => e.Payload.Contains(nameof(ServiceBusClient)) && e.Payload.Contains(client.Identifier)); } }
public async Task AutoLockRenewalWorks(int numThreads) { var lockDuration = TimeSpan.FromSeconds(10); await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: false, lockDuration: lockDuration)) { await using var client = GetClient(); ServiceBusSender sender = client.CreateSender(scope.QueueName); using ServiceBusMessageBatch batch = await sender.CreateBatchAsync(); var messageSendCt = numThreads; ServiceBusMessageBatch messageBatch = AddMessages(batch, messageSendCt); await sender.SendAsync(messageBatch); var options = new ServiceBusProcessorOptions { MaxConcurrentCalls = numThreads, AutoComplete = false }; var processor = client.CreateProcessor(scope.QueueName, options); int messageCt = 0; TaskCompletionSource <bool>[] completionSources = Enumerable .Range(0, numThreads) .Select(index => new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously)) .ToArray(); var completionSourceIndex = -1; processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(); async Task ProcessMessage(ProcessMessageEventArgs args) { try { var message = args.Message; var lockedUntil = message.LockedUntil; await Task.Delay(lockDuration); Assert.That(message.LockedUntil > lockedUntil, $"{lockedUntil},{DateTime.UtcNow}"); await args.CompleteAsync(message, args.CancellationToken); Interlocked.Increment(ref messageCt); } finally { var setIndex = Interlocked.Increment(ref completionSourceIndex); if (setIndex < numThreads) { completionSources[setIndex].SetResult(true); } } } await Task.WhenAll(completionSources.Select(source => source.Task)); await processor.StopProcessingAsync(); Assert.AreEqual(numThreads, messageCt); } }
public async Task MaxAutoLockRenewalDurationRespected(int numThreads, int autoLockRenewalDuration) { var lockDuration = ShortLockDuration; await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: false, lockDuration: lockDuration)) { await using var client = CreateClient(); ServiceBusSender sender = client.CreateSender(scope.QueueName); using ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync(); var messageSendCt = numThreads; ServiceBusMessageBatch messageBatch = ServiceBusTestUtilities.AddMessages(batch, messageSendCt); await sender.SendMessagesAsync(messageBatch); var options = new ServiceBusProcessorOptions { MaxConcurrentCalls = numThreads, AutoCompleteMessages = false, MaxAutoLockRenewalDuration = TimeSpan.FromSeconds(autoLockRenewalDuration) }; await using var processor = client.CreateProcessor(scope.QueueName, options); int messageCt = 0; TaskCompletionSource <bool>[] completionSources = Enumerable .Range(0, numThreads) .Select(index => new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously)) .ToArray(); var completionSourceIndex = -1; processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ServiceBusTestUtilities.ExceptionHandler; await processor.StartProcessingAsync(); async Task ProcessMessage(ProcessMessageEventArgs args) { var message = args.Message; // wait until 5 seconds past the locked until time await Task.Delay(message.LockedUntil.Subtract(DateTimeOffset.UtcNow).Add(TimeSpan.FromSeconds(5))); var lockedUntil = message.LockedUntil; if (!args.CancellationToken.IsCancellationRequested) { // only do the assertion if cancellation wasn't requested as otherwise // the exception we would get is a TaskCanceledException rather than ServiceBusException Assert.AreEqual(lockedUntil, message.LockedUntil); ServiceBusException exception = await AsyncAssert.ThrowsAsync <ServiceBusException>( async() => await args.CompleteMessageAsync(message, args.CancellationToken)); Assert.AreEqual(ServiceBusFailureReason.MessageLockLost, exception.Reason); Interlocked.Increment(ref messageCt); var setIndex = Interlocked.Increment(ref completionSourceIndex); if (setIndex < numThreads) { completionSources[setIndex].SetResult(true); } } } await Task.WhenAll(completionSources.Select(source => source.Task)); await processor.StopProcessingAsync(); // greater or equal because the same message may be received multiple times Assert.GreaterOrEqual(messageCt, numThreads); } }
public async Task ProcessorStopsWhenClientIsClosed(int maxConcurrent) { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: false)) { var messageCompletionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var errorCompletionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); // Create the client and seed the queue with some messages to process. await using var client = CreateClient(60); await SendMessagesAsync(client, scope.QueueName, 100); // Create the processor and define the message handlers. await using var processor = client.CreateProcessor(scope.QueueName, new ServiceBusProcessorOptions { MaxConcurrentCalls = maxConcurrent, AutoCompleteMessages = true, PrefetchCount = 20 }); Task processMessageAsync(ProcessMessageEventArgs args) { // If any message is dispatched for processing, then the // processor is actively reading from the Service Bus // service and should react to the client being closed. messageCompletionSource.TrySetResult(true); return(Task.CompletedTask); } Task processErrorAsync(ProcessErrorEventArgs args) { // If the client is closed, an exception should be thrown // for the disposed connection. This is the condition that // should set the completion source. if ((args.Exception is ObjectDisposedException ex) && ex.ObjectName == nameof(ServiceBusConnection) && ex.Message.StartsWith(Resources.DisposedConnectionMessageProcessorMustStop)) { errorCompletionSource.TrySetResult(true); } return(Task.CompletedTask); } try { processor.ProcessMessageAsync += processMessageAsync; processor.ProcessErrorAsync += processErrorAsync; // Set a cancellation source to use as a safety timer to prevent a // potential test hang; this should not trigger if things are // working correctly. using var cancellationSource = new CancellationTokenSource(); cancellationSource.CancelAfter(TimeSpan.FromMinutes(10)); // Start processing and wait for a message. await processor.StartProcessingAsync(cancellationSource.Token); await messageCompletionSource.Task.AwaitWithCancellation(cancellationSource.Token); // Now that it's been confirmed that the processor is interacting with the // Service Bus service close the client. await client.DisposeAsync(); await errorCompletionSource.Task.AwaitWithCancellation(cancellationSource.Token); // The processor surfaced the expected exception. Verify that it has stopped // processing and cancellation wasn't triggered. This is a non-deterministic // operation, so allow for a few attempts. var attemptsRemaining = 5; while ((attemptsRemaining > 0) && (processor.IsProcessing)) { --attemptsRemaining; await Task.Delay(500, cancellationSource.Token); } Assert.IsFalse(processor.IsProcessing, "The processor should have stopped when the client was closed."); } catch (OperationCanceledException) { Assert.Fail("The cancellation token should not have been triggered."); } finally { if (processor.IsProcessing) { await processor.StopProcessingAsync(); } processor.ProcessMessageAsync -= processMessageAsync; processor.ProcessErrorAsync -= processErrorAsync; } } }
public async Task TransactionGroupSessionProcessorRollback() { var transactionGroup = "myTxn"; await using var client = CreateClient(); await using var queueA = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : true); await using var queueB = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : true); await using var queueC = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : true); var senderA = client.CreateSender(queueA.QueueName); var processorA = client.CreateSessionProcessor(queueA.QueueName, new ServiceBusSessionProcessorOptions { TransactionGroup = transactionGroup }); var senderB = client.CreateSender(queueB.QueueName, new ServiceBusSenderOptions { TransactionGroup = transactionGroup }); var senderC = client.CreateSender(queueC.QueueName, new ServiceBusSenderOptions { TransactionGroup = transactionGroup }); var message = new ServiceBusMessage { SessionId = "sessionId" }; await senderA.SendMessageAsync(message); processorA.ProcessErrorAsync += ExceptionHandler; var tcs = new TaskCompletionSource <bool>(); processorA.ProcessMessageAsync += async args => { using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { await args.CompleteMessageAsync(args.Message); await senderB.SendMessageAsync(message); await senderC.SendMessageAsync(message); } tcs.TrySetResult(true); }; await processorA.StartProcessingAsync(); await tcs.Task; await processorA.StopProcessingAsync(); // transaction wasn't committed - verify that it was rolled back ServiceBusSessionReceiver receiverA = await client.AcceptNextSessionAsync(queueA.QueueName); ServiceBusReceivedMessage receivedMessage = await receiverA.ReceiveMessageAsync(); Assert.IsNotNull(receivedMessage); }
public async Task ProcessMessages(int numThreads, bool autoComplete) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: false)) { await using var client = CreateClient(60); ServiceBusSender sender = client.CreateSender(scope.QueueName); // use double the number of threads so we can make sure we test that we don't // retrieve more messages than expected when there are more messages available using ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync(); var messageSendCt = numThreads * 2; ServiceBusMessageBatch messageBatch = ServiceBusTestUtilities.AddMessages(batch, messageSendCt); await sender.SendMessagesAsync(messageBatch); var options = new ServiceBusProcessorOptions { MaxConcurrentCalls = numThreads, AutoCompleteMessages = autoComplete, PrefetchCount = 20 }; await using var processor = client.CreateProcessor(scope.QueueName, options); int messageCt = 0; TaskCompletionSource <bool>[] completionSources = Enumerable .Range(0, numThreads) .Select(index => new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously)) .ToArray(); var completionSourceIndex = -1; processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ServiceBusTestUtilities.ExceptionHandler; await processor.StartProcessingAsync(); async Task ProcessMessage(ProcessMessageEventArgs args) { try { var message = args.Message; if (!autoComplete) { await args.CompleteMessageAsync(message, args.CancellationToken); } Interlocked.Increment(ref messageCt); } finally { var setIndex = Interlocked.Increment(ref completionSourceIndex); if (setIndex < numThreads) { completionSources[setIndex].SetResult(true); } } } await Task.WhenAll(completionSources.Select(source => source.Task)); var start = DateTime.UtcNow; await processor.StopProcessingAsync(); var stop = DateTime.UtcNow; Assert.Less(stop - start, TimeSpan.FromSeconds(10)); // we complete each task after one message being processed, so the total number of messages // processed should equal the number of threads, but it's possible that we may process a few more per thread. Assert.IsTrue(messageCt >= numThreads); Assert.IsTrue(messageCt <= messageSendCt, messageCt.ToString()); } }
public async Task TransactionGroupReceivesFirst(bool partitioned, bool enableSessions) { var transactionGroup = "myTxn"; await using var client = CreateClient(); await using var queueA = await ServiceBusScope.CreateWithQueue(enablePartitioning : partitioned, enableSession : enableSessions); await using var queueB = await ServiceBusScope.CreateWithQueue(enablePartitioning : partitioned, enableSession : enableSessions); await using var topicC = await ServiceBusScope.CreateWithTopic(enablePartitioning : partitioned, enableSession : enableSessions); var senderA = client.CreateSender(queueA.QueueName); ServiceBusReceiver receiverA = null; if (!enableSessions) { receiverA = client.CreateReceiver(queueA.QueueName, new ServiceBusReceiverOptions { TransactionGroup = transactionGroup }); } var senderB = client.CreateSender(queueB.QueueName, new ServiceBusSenderOptions { TransactionGroup = transactionGroup }); var senderC = client.CreateSender(topicC.TopicName, new ServiceBusSenderOptions { TransactionGroup = transactionGroup }); var message = new ServiceBusMessage { SessionId = enableSessions ? "sessionId" : null, TransactionPartitionKey = partitioned ? "sessionId" : null }; await senderA.SendMessageAsync(message); if (enableSessions) { receiverA = await client.AcceptNextSessionAsync(queueA.QueueName, new ServiceBusSessionReceiverOptions { TransactionGroup = transactionGroup }); } ServiceBusReceivedMessage receivedMessage = await receiverA.ReceiveMessageAsync(); // If the transaction succeeds, then all the operations occurred on the same partition. using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { await receiverA.CompleteMessageAsync(receivedMessage); await senderB.SendMessageAsync(message); await senderC.SendMessageAsync(message); ts.Complete(); } receivedMessage = await receiverA.ReceiveMessageAsync(); Assert.IsNull(receivedMessage); }
public async Task TransactionGroupSessionProcessor() { var transactionGroup = "myTxn"; await using var client = CreateClient(); await using var queueA = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : true); await using var queueB = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : true); await using var queueC = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : true); var senderA = client.CreateSender(queueA.QueueName); var processorA = client.CreateSessionProcessor(queueA.QueueName, new ServiceBusSessionProcessorOptions { TransactionGroup = transactionGroup }); var senderB = client.CreateSender(queueB.QueueName, new ServiceBusSenderOptions { TransactionGroup = transactionGroup }); var senderC = client.CreateSender(queueC.QueueName, new ServiceBusSenderOptions { TransactionGroup = transactionGroup }); var message = new ServiceBusMessage { SessionId = "sessionId" }; await senderA.SendMessageAsync(message); processorA.ProcessErrorAsync += ExceptionHandler; var tcs = new TaskCompletionSource <bool>(); processorA.ProcessMessageAsync += async args => { using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { await args.CompleteMessageAsync(args.Message); await senderB.SendMessageAsync(message); await senderC.SendMessageAsync(message); ts.Complete(); } tcs.TrySetResult(true); }; await processorA.StartProcessingAsync(); await tcs.Task; await processorA.StopProcessingAsync(); // this should timeout as the session message was completed Assert.ThrowsAsync <ServiceBusException>( async() => await client.AcceptNextSessionAsync(queueA.QueueName)); // should not throw _ = await client.AcceptNextSessionAsync(queueB.QueueName); _ = await client.AcceptNextSessionAsync(queueC.QueueName); }
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"); } }
public async Task ReceiverCanReconnectToSameSession(bool isSessionSpecified) { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: true, lockDuration: TimeSpan.FromSeconds(5))) { await using var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); ServiceBusSender sender = client.CreateSender(scope.QueueName); await sender.SendMessagesAsync(GetMessages(3, "sessionId1")); // send another session message before the one we are interested in to make sure that when isSessionSpecified=true, it is being respected await sender.SendMessagesAsync(GetMessages(3, "sessionId2")); ServiceBusSessionReceiver receiver = await client.AcceptSessionAsync( scope.QueueName, isSessionSpecified? "sessionId1" : null); if (isSessionSpecified) { Assert.AreEqual("sessionId1", receiver.SessionId); } var firstMessage = await receiver.ReceiveMessageAsync(); Assert.AreEqual(receiver.SessionId, firstMessage.SessionId); var sessionId = receiver.SessionId; await Task.Delay((receiver.SessionLockedUntil - DateTime.UtcNow) + TimeSpan.FromSeconds(5)); var secondMessage = await receiver.ReceiveMessageAsync(); Assert.AreEqual(sessionId, receiver.SessionId); Assert.AreEqual(receiver.SessionId, secondMessage.SessionId); // Even though the receiver has re-established the session link, since the first message was // received before we reconnected, the message won't be able to be settled. This is analogous // to the case of reconnecting a regular non-session link and getting a MessageLockLost error. Assert.That( async() => await receiver.CompleteMessageAsync(firstMessage), Throws.InstanceOf <ServiceBusException>().And.Property(nameof(ServiceBusException.Reason)).EqualTo(ServiceBusFailureReason.SessionLockLost)); await Task.Delay((receiver.SessionLockedUntil - DateTime.UtcNow) + TimeSpan.FromSeconds(5)); // If another receiver accepts the session after the lock is lost, we expect a SessionCannotBeLocked error, // when we try to receive again with our initial receiver. ServiceBusSessionReceiver secondReceiver = await client.AcceptSessionAsync( scope.QueueName, sessionId); try { await receiver.ReceiveMessageAsync(); } catch (ServiceBusException ex) when(ex.Reason == ServiceBusFailureReason.SessionCannotBeLocked) { return; } catch (Exception ex) { Assert.Fail($"Expected exception not thrown: {ex}"); } Assert.Fail("No exception thrown!"); } }
public async Task CanUpdateConcurrency() { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: false)) { await using var client = CreateClient(); var sender = client.CreateSender(scope.QueueName); int messageCount = 200; await sender.SendMessagesAsync(ServiceBusTestUtilities.GetMessages(messageCount)); await using var processor = client.CreateProcessor(scope.QueueName, new ServiceBusProcessorOptions { MaxConcurrentCalls = 20 }); int receivedCount = 0; var tcs = new TaskCompletionSource <bool>(); async Task ProcessMessage(ProcessMessageEventArgs args) { if (args.CancellationToken.IsCancellationRequested) { await args.AbandonMessageAsync(args.Message); } var count = Interlocked.Increment(ref receivedCount); if (count == messageCount) { tcs.SetResult(true); } // decrease concurrency if (count == 100) { processor.UpdateConcurrency(1); Assert.AreEqual(1, processor.MaxConcurrentCalls); } // increase concurrency if (count == 150) { Assert.LessOrEqual(processor._tasks.Where(t => !t.Task.IsCompleted).Count(), 1); processor.UpdateConcurrency(10); Assert.AreEqual(10, processor.MaxConcurrentCalls); } if (count == 175) { Assert.GreaterOrEqual(processor._tasks.Where(t => !t.Task.IsCompleted).Count(), 5); } } processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ServiceBusTestUtilities.ExceptionHandler; await processor.StartProcessingAsync(); await tcs.Task; await processor.StopProcessingAsync(); } }
public async Task LogsSessionEvents() { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: true)) { await using var client = GetNoRetryClient(); _listener.SingleEventById(ServiceBusEventSource.ClientCreateStartEvent, e => e.Payload.Contains(nameof(ServiceBusClient)) && e.Payload.Contains(client.FullyQualifiedNamespace)); var messageCount = 10; ServiceBusSender sender = client.CreateSender(scope.QueueName); _listener.SingleEventById(ServiceBusEventSource.ClientCreateStartEvent, e => e.Payload.Contains(nameof(ServiceBusSender)) && e.Payload.Contains(sender.FullyQualifiedNamespace) && e.Payload.Contains(sender.EntityPath)); using ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync(); _listener.SingleEventById(ServiceBusEventSource.CreateMessageBatchStartEvent, e => e.Payload.Contains(sender.Identifier)); _listener.SingleEventById(ServiceBusEventSource.CreateMessageBatchCompleteEvent, e => e.Payload.Contains(sender.Identifier)); IEnumerable <ServiceBusMessage> messages = AddMessages(batch, messageCount, "sessionId").AsEnumerable <ServiceBusMessage>(); await sender.SendMessagesAsync(batch); _listener.SingleEventById(ServiceBusEventSource.CreateSendLinkStartEvent, e => e.Payload.Contains(sender.Identifier)); _listener.SingleEventById(ServiceBusEventSource.CreateSendLinkCompleteEvent, e => e.Payload.Contains(sender.Identifier)); _listener.SingleEventById(ServiceBusEventSource.SendMessageStartEvent, e => e.Payload.Contains(sender.Identifier)); _listener.SingleEventById(ServiceBusEventSource.SendMessageCompleteEvent, e => e.Payload.Contains(sender.Identifier)); var receiver = client.CreateReceiver(scope.QueueName); _listener.SingleEventById(ServiceBusEventSource.ClientCreateStartEvent, e => e.Payload.Contains(nameof(ServiceBusReceiver)) && e.Payload.Contains(client.FullyQualifiedNamespace)); // can't use a non-session receiver for session queue Assert.That( async() => await receiver.ReceiveMessageAsync(), Throws.InstanceOf <InvalidOperationException>()); _listener.SingleEventById(ServiceBusEventSource.CreateReceiveLinkStartEvent, e => e.Payload.Contains(receiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.CreateReceiveLinkExceptionEvent, e => e.Payload.Contains(receiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.ReceiveMessageStartEvent, e => e.Payload.Contains(receiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.ReceiveMessageExceptionEvent, e => e.Payload.Contains(receiver.Identifier)); var sessionReceiver = await client.CreateSessionReceiverAsync(scope.QueueName); _listener.EventsById(ServiceBusEventSource.ClientCreateStartEvent).Where(e => e.Payload.Contains(nameof(ServiceBusSessionReceiver)) && e.Payload.Contains(client.FullyQualifiedNamespace)).Any(); _listener.SingleEventById(ServiceBusEventSource.CreateReceiveLinkStartEvent, e => e.Payload.Contains(sessionReceiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.CreateReceiveLinkCompleteEvent, e => e.Payload.Contains(sessionReceiver.Identifier)); var msg = await sessionReceiver.ReceiveMessageAsync(); _listener.SingleEventById(ServiceBusEventSource.ReceiveMessageStartEvent, e => e.Payload.Contains(sessionReceiver.Identifier)); msg = await sessionReceiver.PeekMessageAsync(); _listener.SingleEventById(ServiceBusEventSource.CreateManagementLinkStartEvent, e => e.Payload.Contains(sessionReceiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.CreateManagementLinkCompleteEvent, e => e.Payload.Contains(sessionReceiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.PeekMessageStartEvent, e => e.Payload.Contains(sessionReceiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.PeekMessageCompleteEvent, e => e.Payload.Contains(sessionReceiver.Identifier)); await receiver.DisposeAsync(); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeStartEvent, e => e.Payload.Contains(nameof(ServiceBusReceiver)) && e.Payload.Contains(receiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeCompleteEvent, e => e.Payload.Contains(nameof(ServiceBusReceiver)) && e.Payload.Contains(receiver.Identifier)); await sessionReceiver.DisposeAsync(); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeStartEvent, e => e.Payload.Contains(nameof(ServiceBusSessionReceiver)) && e.Payload.Contains(sessionReceiver.Identifier)); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeCompleteEvent, e => e.Payload.Contains(nameof(ServiceBusSessionReceiver)) && e.Payload.Contains(sessionReceiver.Identifier)); await Task.Delay(TimeSpan.FromSeconds(2)); _listener.SingleEventById(ServiceBusEventSource.ReceiveLinkClosedEvent, e => e.Payload.Contains(sessionReceiver.Identifier) && e.Payload.Contains(sessionReceiver.SessionId)); await sender.DisposeAsync(); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeStartEvent, e => e.Payload.Contains(nameof(ServiceBusSender)) && e.Payload.Contains(sender.Identifier)); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeCompleteEvent, e => e.Payload.Contains(nameof(ServiceBusSender)) && e.Payload.Contains(sender.Identifier)); await client.DisposeAsync(); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeStartEvent, e => e.Payload.Contains(nameof(ServiceBusClient)) && e.Payload.Contains(client.Identifier)); _listener.SingleEventById(ServiceBusEventSource.ClientDisposeCompleteEvent, e => e.Payload.Contains(nameof(ServiceBusClient)) && e.Payload.Contains(client.Identifier)); } }
public async Task AutoLockRenewalWorks(int numThreads) { var lockDuration = TimeSpan.FromSeconds(10); await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: false, lockDuration: lockDuration)) { await using var client = GetClient(); ServiceBusSender sender = client.CreateSender(scope.QueueName); using ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync(); var messageSendCt = numThreads; ServiceBusMessageBatch messageBatch = AddMessages(batch, messageSendCt); await sender.SendMessagesAsync(messageBatch); var options = new ServiceBusProcessorOptions { MaxConcurrentCalls = numThreads, AutoCompleteMessages = false }; await using var processor = client.CreateProcessor(scope.QueueName, options); int messageCt = 0; TaskCompletionSource <bool>[] completionSources = Enumerable .Range(0, numThreads) .Select(index => new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously)) .ToArray(); var completionSourceIndex = -1; processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += args => { // If the connection drops due to network flakiness // after the message is received but before we // complete it, we will get a message lock // lost exception. We are still able to verify // that the message will be completed eventually. var exception = (ServiceBusException)args.Exception; if (!(args.Exception is ServiceBusException sbEx) || sbEx.Reason != ServiceBusFailureReason.MessageLockLost) { Assert.Fail(args.Exception.ToString()); } return(Task.CompletedTask); }; await processor.StartProcessingAsync(); async Task ProcessMessage(ProcessMessageEventArgs args) { var message = args.Message; var lockedUntil = message.LockedUntil; await Task.Delay(lockDuration); await args.CompleteMessageAsync(message, args.CancellationToken); Interlocked.Increment(ref messageCt); var setIndex = Interlocked.Increment(ref completionSourceIndex); completionSources[setIndex].SetResult(true); } await Task.WhenAll(completionSources.Select(source => source.Task)); Assert.IsTrue(processor.IsProcessing); await processor.StopProcessingAsync(); Assert.IsFalse(processor.IsProcessing); Assert.AreEqual(numThreads, messageCt); } }
public async Task ProcessSessionMessages() { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: true)) { string connectionString = TestEnvironment.ServiceBusConnectionString; string queueName = scope.QueueName; await using var client = GetClient(); #region Snippet:ServiceBusProcessSessionMessages //@@ string connectionString = "<connection_string>"; //@@ string queueName = "<queue_name>"; // since ServiceBusClient implements IAsyncDisposable we create it with "await using" //@@ await using var client = new ServiceBusClient(connectionString); // create the sender ServiceBusSender sender = client.CreateSender(queueName); // create a message batch that we can send ServiceBusMessageBatch messageBatch = await sender.CreateBatchAsync(); messageBatch.TryAdd( new ServiceBusMessage(Encoding.UTF8.GetBytes("First")) { SessionId = "Session1" }); messageBatch.TryAdd( new ServiceBusMessage(Encoding.UTF8.GetBytes("Second")) { SessionId = "Session2" }); // send the message batch await sender.SendAsync(messageBatch); // get the options to use for configuring the processor var options = new ServiceBusProcessorOptions { // By default after the message handler returns, the processor will complete the message // If I want more fine-grained control over settlement, I can set this to false. AutoComplete = false, // I can also allow for multi-threading MaxConcurrentCalls = 2 }; // create a session processor that we can use to process the messages ServiceBusSessionProcessor processor = client.CreateSessionProcessor(queueName, options); // since the message handler will run in a background thread, in order to prevent // this sample from terminating immediately, we can use a task completion source that // we complete from within the message handler. TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); processor.ProcessMessageAsync += MessageHandler; processor.ProcessErrorAsync += ErrorHandler; async Task MessageHandler(ProcessSessionMessageEventArgs args) { var body = args.Message.Body.AsString(); // we can evaluate application logic and use that to determine how to settle the message. await args.CompleteAsync(args.Message); // we can also set arbitrary session state using this receiver // the state is specific to the session, and not any particular message await args.SetSessionStateAsync(Encoding.Default.GetBytes("some state")); tcs.SetResult(true); } Task ErrorHandler(ProcessErrorEventArgs args) { // the error source tells me at what point in the processing an error occurred Console.WriteLine(args.ErrorSource); // the fully qualified namespace is available Console.WriteLine(args.FullyQualifiedNamespace); // as well as the entity path Console.WriteLine(args.EntityPath); Console.WriteLine(args.Exception.ToString()); return(Task.CompletedTask); } await processor.StartProcessingAsync(); // await our task completion source task so that the message handler will be invoked at least once. await tcs.Task; // stop processing once the task completion source was completed. await processor.StopProcessingAsync(); #endregion } }
public async Task ProcessEvent(int numThreads, bool autoComplete) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: true)) { await using var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); ServiceBusSender sender = client.GetSender(scope.QueueName); // send 1 message for each thread and use a different session for each message ConcurrentDictionary <string, bool> sessions = new ConcurrentDictionary <string, bool>(); for (int i = 0; i < numThreads; i++) { var sessionId = Guid.NewGuid().ToString(); await sender.SendAsync(GetMessage(sessionId)); sessions.TryAdd(sessionId, true); } var options = new ServiceBusProcessorOptions { MaxConcurrentCalls = numThreads, AutoComplete = autoComplete }; var processor = client.GetSessionProcessor(scope.QueueName, options); int messageCt = 0; TaskCompletionSource <bool>[] completionSources = Enumerable .Range(0, numThreads) .Select(index => new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously)) .ToArray(); var completionSourceIndex = -1; processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(); async Task ProcessMessage(ProcessMessageEventArgs args) { try { var message = args.Message; var receiver = args.Receiver; if (!autoComplete) { await receiver.CompleteAsync(message.LockToken); } Interlocked.Increment(ref messageCt); sessions.TryRemove(message.SessionId, out bool _); var session = receiver.GetSessionManager(); Assert.AreEqual(message.SessionId, session.SessionId); Assert.IsNotNull(session.LockedUntil); } finally { var setIndex = Interlocked.Increment(ref completionSourceIndex); completionSources[setIndex].SetResult(true); } } await Task.WhenAll(completionSources.Select(source => source.Task)); await processor.StopProcessingAsync(); // there is only one message for each session, and one // thread for each session, so the total messages processed // should equal the number of threads Assert.AreEqual(numThreads, messageCt); // we should have received messages from each of the sessions Assert.AreEqual(0, sessions.Count); } }
public async Task MaxAutoLockRenewalDurationRespected(int numThreads, int autoLockRenewalDuration) { var lockDuration = TimeSpan.FromSeconds(10); await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: false, lockDuration: lockDuration)) { await using var client = GetClient(); ServiceBusSender sender = client.CreateSender(scope.QueueName); using ServiceBusMessageBatch batch = await sender.CreateBatchAsync(); var messageSendCt = numThreads; ServiceBusMessageBatch messageBatch = AddMessages(batch, messageSendCt); await sender.SendAsync(messageBatch); var options = new ServiceBusProcessorOptions { MaxConcurrentCalls = numThreads, AutoComplete = false, MaxAutoLockRenewalDuration = TimeSpan.FromSeconds(autoLockRenewalDuration) }; var processor = client.CreateProcessor(scope.QueueName, options); int messageCt = 0; TaskCompletionSource <bool>[] completionSources = Enumerable .Range(0, numThreads) .Select(index => new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously)) .ToArray(); var completionSourceIndex = -1; processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(); async Task ProcessMessage(ProcessMessageEventArgs args) { try { var message = args.Message; var lockedUntil = message.LockedUntil; await Task.Delay(lockDuration.Add(TimeSpan.FromSeconds(1))); if (!args.CancellationToken.IsCancellationRequested) { // only do the assertion if cancellation wasn't requested as otherwise // the exception we would get is a TaskCanceledException rather than ServiceBusException Assert.AreEqual(lockedUntil, message.LockedUntil); Assert.That( async() => await args.CompleteAsync(message, args.CancellationToken), Throws.InstanceOf <ServiceBusException>().And.Property(nameof(ServiceBusException.Reason)).EqualTo(ServiceBusException.FailureReason.MessageLockLost)); Interlocked.Increment(ref messageCt); } } finally { var setIndex = Interlocked.Increment(ref completionSourceIndex); if (setIndex < numThreads) { completionSources[setIndex].SetResult(true); } } } await Task.WhenAll(completionSources.Select(source => source.Task)); await processor.StopProcessingAsync(); Assert.AreEqual(numThreads, messageCt); } }
public async Task ProcessEventConsumesAllMessages(int numThreads, bool autoComplete) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: true)) { await using var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); ServiceBusSender sender = client.GetSender(scope.QueueName); // send 1 message for each thread and use a different session for each message ConcurrentDictionary <string, bool> sessions = new ConcurrentDictionary <string, bool>(); for (int i = 0; i < numThreads; i++) { var sessionId = Guid.NewGuid().ToString(); await sender.SendAsync(GetMessage(sessionId)); sessions.TryAdd(sessionId, true); } int messageCt = 0; TaskCompletionSource <bool> taskCompletionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var options = new ServiceBusProcessorOptions { MaxConcurrentCalls = numThreads, AutoComplete = autoComplete }; ServiceBusProcessor processor = GetNoRetryClient().GetSessionProcessor(scope.QueueName, options); processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(); async Task ProcessMessage(ProcessMessageEventArgs args) { try { var receiver = args.Receiver; var message = args.Message; if (!autoComplete) { await receiver.CompleteAsync(message.LockToken); } sessions.TryRemove(message.SessionId, out bool _); var session = receiver.GetSessionManager(); Assert.AreEqual(message.SessionId, session.SessionId); Assert.IsNotNull(session.LockedUntil); } finally { var ct = Interlocked.Increment(ref messageCt); if (ct == numThreads) { taskCompletionSource.SetResult(true); } } } await taskCompletionSource.Task; await processor.StopProcessingAsync(); Assert.AreEqual(numThreads, messageCt); // we should have received messages from each of the sessions Assert.AreEqual(0, sessions.Count); // try receiving to verify empty // since all the messages are gone and we are using sessions, we won't actually // be able to open the Receive link // only do this assertion when we complete the message ourselves, // otherwise the message completion may have been cancelled if it didn't finish // before calling StopProcessingAsync. if (!autoComplete) { Assert.That(async() => await GetNoRetryClient().GetSessionReceiverAsync(scope.QueueName), Throws.Exception); } } }
public async Task UserSettlingWithAutoCompleteDoesNotThrow(int numThreads) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: false)) { await using var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); ServiceBusSender sender = client.CreateSender(scope.QueueName); // use double the number of threads so we can make sure we test that we don't // retrieve more messages than expected when there are more messages available using ServiceBusMessageBatch batch = await sender.CreateBatchAsync(); var messageSendCt = numThreads * 2; ServiceBusMessageBatch messageBatch = AddMessages(batch, messageSendCt); await sender.SendAsync(messageBatch); var options = new ServiceBusProcessorOptions { MaxConcurrentCalls = numThreads, AutoComplete = true, MaxReceiveWaitTime = TimeSpan.FromSeconds(30) }; var processor = client.CreateProcessor(scope.QueueName, options); int messageCt = 0; TaskCompletionSource <bool>[] completionSources = Enumerable .Range(0, numThreads) .Select(index => new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously)) .ToArray(); var completionSourceIndex = -1; processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(); async Task ProcessMessage(ProcessMessageEventArgs args) { try { var message = args.Message; switch (numThreads) { case 1: await args.CompleteAsync(message, args.CancellationToken); break; case 5: await args.AbandonAsync(message); break; case 10: await args.DeadLetterAsync(message); break; case 20: await args.DeferAsync(message); break; } Interlocked.Increment(ref messageCt); } finally { var setIndex = Interlocked.Increment(ref completionSourceIndex); if (setIndex < numThreads) { completionSources[setIndex].SetResult(true); } } } await Task.WhenAll(completionSources.Select(source => source.Task)); await processor.StopProcessingAsync(); // we complete each task after one message being processed, so the total number of messages // processed should equal the number of threads, but it's possible that we may process a few more per thread. Assert.IsTrue(messageCt >= numThreads); Assert.IsTrue(messageCt < messageSendCt); } }
public async Task Process_Event_SessionId(int numThreads, bool autoComplete) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: true)) { await using var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); ServiceBusSender sender = client.GetSender(scope.QueueName); // send 1 message for each thread and use a different session for each message ConcurrentDictionary <string, bool> sessions = new ConcurrentDictionary <string, bool>(); string sessionId = null; for (int i = 0; i < numThreads; i++) { sessionId = Guid.NewGuid().ToString(); await sender.SendAsync(GetMessage(sessionId)); sessions.TryAdd(sessionId, true); } int messageCt = 0; TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var options = new ServiceBusProcessorOptions { MaxConcurrentCalls = numThreads, AutoComplete = autoComplete, }; var processor = client.GetSessionProcessor( scope.QueueName, options, sessionId); // using the last sessionId from the loop processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(); async Task ProcessMessage(ProcessMessageEventArgs args) { try { var receiver = args.Receiver; var message = args.Message; if (!autoComplete) { await receiver.CompleteAsync(message); } sessions.TryRemove(message.SessionId, out bool _); Assert.AreEqual(sessionId, message.SessionId); var session = receiver.GetSessionManager(); Assert.AreEqual(sessionId, session.SessionId); Assert.IsNotNull(session.LockedUntil); } finally { var ct = Interlocked.Increment(ref messageCt); tcs.SetResult(true); } } await tcs.Task; await processor.StopProcessingAsync(); // only one message has the session id that we // configured the processor with Assert.AreEqual(1, messageCt); // we should have received messages from only the specified session Assert.AreEqual(numThreads - 1, sessions.Count); } }
public async Task CrossEntityTransactionSendsFirst(bool partitioned, bool enableSessions) { await using var client = CreateCrossEntityTxnClient(); await using var queueA = await ServiceBusScope.CreateWithQueue(enablePartitioning : partitioned, enableSession : enableSessions); await using var queueB = await ServiceBusScope.CreateWithQueue(enablePartitioning : partitioned, enableSession : enableSessions); await using var queueC = await ServiceBusScope.CreateWithQueue(enablePartitioning : partitioned, enableSession : enableSessions); await using var noTxClient = CreateClient(); var senderA = noTxClient.CreateSender(queueA.QueueName); ServiceBusReceiver receiverA = null; ServiceBusReceiver receiverB = null; ServiceBusReceiver receiverC = null; if (!enableSessions) { receiverA = client.CreateReceiver(queueA.QueueName); receiverB = client.CreateReceiver(queueB.QueueName); receiverC = noTxClient.CreateReceiver(queueC.QueueName); } var senderB = client.CreateSender(queueB.QueueName); var senderC = client.CreateSender(queueC.QueueName); var message = new ServiceBusMessage { SessionId = enableSessions ? "sessionId" : null, TransactionPartitionKey = partitioned ? "sessionId" : null }; // B is the send via entity since it is first await senderB.SendMessageAsync(message); await senderA.SendMessageAsync(message); if (enableSessions) { // you can't use a receiver after a sender (for a different entity) when using a Transaction Group because it would be // saying that you want to receive via the sender entity which isn't possible Assert.ThrowsAsync <InvalidOperationException>( async() => await client.AcceptNextSessionAsync(queueA.QueueName)); receiverB = await client.AcceptNextSessionAsync(queueB.QueueName); } else { Assert.ThrowsAsync <InvalidOperationException>(async() => await receiverA.ReceiveMessageAsync()); } // After the above throws, the session gets closed by the AMQP lib, so we are testing whether the fault tolerant session/controller // objects get re-created correctly. ServiceBusReceivedMessage receivedMessageB = await receiverB.ReceiveMessageAsync(); // If the transaction succeeds, then all the operations occurred on the same partition. using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { // this is allowed because it is on B await receiverB.CompleteMessageAsync(receivedMessageB); // send to C via B - this is allowed because we are sending await senderC.SendMessageAsync(message); ts.Complete(); } if (enableSessions) { receiverC = await noTxClient.AcceptNextSessionAsync(queueC.QueueName); } var receivedMessageC = await receiverC.ReceiveMessageAsync(); Assert.IsNotNull(receivedMessageC); receivedMessageB = await receiverB.ReceiveMessageAsync(); Assert.IsNull(receivedMessageB); await senderB.SendMessageAsync(message); // If the transaction succeeds, then all the operations occurred on the same partition. using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { receivedMessageB = await receiverB.ReceiveMessageAsync(); // this is allowed because it is on B await receiverB.CompleteMessageAsync(receivedMessageB); // this will fail because it is not part of txn group Assert.ThrowsAsync <ServiceBusException>(async() => await senderA.SendMessageAsync(message)); ts.Complete(); } }
public async Task ProcessMessages() { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: false)) { string connectionString = TestEnvironment.ServiceBusConnectionString; string queueName = scope.QueueName; await using var client = CreateClient(); #region Snippet:ServiceBusProcessMessages //@@ string connectionString = "<connection_string>"; //@@ string queueName = "<queue_name>"; // since ServiceBusClient implements IAsyncDisposable we create it with "await using" //@@ await using var client = new ServiceBusClient(connectionString); // create the sender ServiceBusSender sender = client.CreateSender(queueName); // create a set of messages that we can send ServiceBusMessage[] messages = new ServiceBusMessage[] { new ServiceBusMessage("First"), new ServiceBusMessage("Second") }; // send the message batch await sender.SendMessagesAsync(messages); #region Snippet:ServiceBusConfigureProcessor // create the options to use for configuring the processor var options = new ServiceBusProcessorOptions { // By default or when AutoCompleteMessages is set to true, the processor will complete the message after executing the message handler // Set AutoCompleteMessages to false to [settle messages](https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-transfers-locks-settlement#peeklock) on your own. // In both cases, if the message handler throws an exception without settling the message, the processor will abandon the message. AutoCompleteMessages = false, // I can also allow for multi-threading MaxConcurrentCalls = 2 }; // create a processor that we can use to process the messages await using ServiceBusProcessor processor = client.CreateProcessor(queueName, options); // configure the message and error handler to use processor.ProcessMessageAsync += MessageHandler; processor.ProcessErrorAsync += ErrorHandler; async Task MessageHandler(ProcessMessageEventArgs args) { string body = args.Message.Body.ToString(); Console.WriteLine(body); // we can evaluate application logic and use that to determine how to settle the message. await args.CompleteMessageAsync(args.Message); } Task ErrorHandler(ProcessErrorEventArgs args) { // the error source tells me at what point in the processing an error occurred Console.WriteLine(args.ErrorSource); // the fully qualified namespace is available Console.WriteLine(args.FullyQualifiedNamespace); // as well as the entity path Console.WriteLine(args.EntityPath); Console.WriteLine(args.Exception.ToString()); return(Task.CompletedTask); } // start processing await processor.StartProcessingAsync(); // since the processing happens in the background, we add a Conole.ReadKey to allow the processing to continue until a key is pressed. Console.ReadKey(); #endregion #endregion } }
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); 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); remaining -= received.Count; } 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); (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); (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); (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); (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); (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); (string Key, object Value, DiagnosticListener)renewStop = _listener.Events.Dequeue(); Assert.AreEqual(DiagnosticProperty.RenewSessionLockActivityName + ".Stop", renewStop.Key); // set state var state = new BinaryData("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); (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.ToArray(), getState.ToArray()); (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); (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); (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>()); }; }
public async Task DeadLetterMessages(bool useSpecificSession) { 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 messageCount = 10; var sessionId = "sessionId1"; using ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync(); IEnumerable <ServiceBusMessage> messages = AddMessages(batch, messageCount, sessionId).AsEnumerable <ServiceBusMessage>(); await sender.SendMessagesAsync(batch); var receiver = await client.AcceptSessionAsync( scope.QueueName, useSpecificSession?sessionId : null); var remainingMessages = messageCount; var messageEnum = messages.GetEnumerator(); while (remainingMessages > 0) { foreach (var item in await receiver.ReceiveMessagesAsync(remainingMessages)) { remainingMessages--; messageEnum.MoveNext(); Assert.AreEqual(messageEnum.Current.MessageId, item.MessageId); Assert.AreEqual(messageEnum.Current.SessionId, item.SessionId); await receiver.DeadLetterMessageAsync(item.LockToken, "testReason", "testDescription"); } } Assert.AreEqual(0, remainingMessages); var peekedMessage = receiver.PeekMessageAsync(); Assert.IsNull(peekedMessage.Result); messageEnum.Reset(); remainingMessages = messageCount; var deadLetterReceiver = client.CreateReceiver(scope.QueueName, new ServiceBusReceiverOptions { SubQueue = SubQueue.DeadLetter }); while (remainingMessages > 0) { foreach (var msg in await deadLetterReceiver.ReceiveMessagesAsync(remainingMessages)) { remainingMessages--; messageEnum.MoveNext(); Assert.AreEqual(messageEnum.Current.MessageId, msg.MessageId); Assert.AreEqual(messageEnum.Current.SessionId, msg.SessionId); Assert.AreEqual("testReason", msg.DeadLetterReason); Assert.AreEqual("testDescription", msg.DeadLetterErrorDescription); await deadLetterReceiver.CompleteMessageAsync(msg.LockToken); } } Assert.AreEqual(0, remainingMessages); var deadLetterMessage = await deadLetterReceiver.PeekMessageAsync(); Assert.IsNull(deadLetterMessage); } }
public async Task TransactionalSendViaCommitTest() { var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); await using var intermediateQueue = await ServiceBusScope.CreateWithQueue(enablePartitioning : true, enableSession : false); await using var destination1 = await ServiceBusScope.CreateWithTopic(enablePartitioning : true, enableSession : false); await using var destination2 = await ServiceBusScope.CreateWithQueue(enablePartitioning : true, enableSession : false); var intermediateSender = client.CreateSender(intermediateQueue.QueueName); var intermediateReceiver = client.CreateReceiver(intermediateQueue.QueueName); var destination1Sender = client.CreateSender(destination1.TopicName); var destination1ViaSender = client.CreateSender(destination1.TopicName, intermediateQueue.QueueName); var destination2ViaSender = client.CreateSender(destination2.QueueName, intermediateQueue.QueueName); var destination1Receiver = client.CreateReceiver(destination1.TopicName, destination1.SubscriptionNames.First()); var destination2Receiver = client.CreateReceiver(destination2.QueueName); var body = Encoding.Default.GetBytes(Guid.NewGuid().ToString("N")); var message1 = new ServiceBusMessage(body) { MessageId = "1", PartitionKey = "pk1" }; var message2 = new ServiceBusMessage(body) { MessageId = "2", PartitionKey = "pk2", ViaPartitionKey = "pk1" }; var message3 = new ServiceBusMessage(body) { MessageId = "3", PartitionKey = "pk3", ViaPartitionKey = "pk1" }; await intermediateSender.SendAsync(message1).ConfigureAwait(false); var receivedMessage = await intermediateReceiver.ReceiveAsync(); Assert.NotNull(receivedMessage); Assert.AreEqual("pk1", receivedMessage.PartitionKey); // If the transaction succeeds, then all the operations occurred on the same partition. using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { await intermediateReceiver.CompleteAsync(receivedMessage); await destination1ViaSender.SendAsync(message2); await destination2ViaSender.SendAsync(message3); ts.Complete(); } // Assert that first message indeed completed. receivedMessage = await intermediateReceiver.ReceiveAsync(TimeSpan.FromSeconds(5)); Assert.Null(receivedMessage); // Assert that second message reached its destination. var receivedMessage1 = await destination1Receiver.ReceiveAsync(); Assert.NotNull(receivedMessage1); Assert.AreEqual("pk2", receivedMessage1.PartitionKey); // Assert destination1 message actually used partitionKey in the destination entity. var destination1Message = new ServiceBusMessage(body) { PartitionKey = "pk2" }; using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { await destination1Receiver.CompleteAsync(receivedMessage1); await destination1Sender.SendAsync(destination1Message); ts.Complete(); } // Assert that third message reached its destination. var receivedMessage2 = await destination2Receiver.ReceiveAsync(); Assert.NotNull(receivedMessage2); Assert.AreEqual("pk3", receivedMessage2.PartitionKey); await destination2Receiver.CompleteAsync(receivedMessage2); // Cleanup receivedMessage1 = await destination1Receiver.ReceiveAsync(); await destination1Receiver.CompleteAsync(receivedMessage1); }
public async Task CrossEntityTransactionProcessor() { await using var client = CreateCrossEntityTxnClient(); await using var noTxClient = CreateClient(); await using var queueA = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : false); await using var queueB = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : false); await using var queueC = await ServiceBusScope.CreateWithQueue(enablePartitioning : false, enableSession : false); var senderA = noTxClient.CreateSender(queueA.QueueName); await using var processorA = client.CreateProcessor(queueA.QueueName); var receiverA = noTxClient.CreateReceiver(queueA.QueueName); var receiverB = noTxClient.CreateReceiver(queueB.QueueName); var receiverC = noTxClient.CreateReceiver(queueC.QueueName); var senderB = client.CreateSender(queueB.QueueName); var senderC = client.CreateSender(queueC.QueueName); var message = new ServiceBusMessage(); await senderA.SendMessageAsync(message); processorA.ProcessErrorAsync += ServiceBusTestUtilities.ExceptionHandler; var tcs = new TaskCompletionSource <bool>(); processorA.ProcessMessageAsync += async args => { using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { await args.CompleteMessageAsync(args.Message); await senderB.SendMessageAsync(message); await senderC.SendMessageAsync(message); ts.Complete(); } tcs.TrySetResult(true); }; await processorA.StartProcessingAsync(); await tcs.Task; await processorA.StopProcessingAsync(); ServiceBusReceivedMessage receivedMessage = await receiverA.ReceiveMessageAsync(); Assert.IsNull(receivedMessage); receivedMessage = await receiverB.ReceiveMessageAsync(); Assert.IsNotNull(receivedMessage); receivedMessage = await receiverC.ReceiveMessageAsync(); Assert.IsNotNull(receivedMessage); }