public async Task Receive_StopProcessing(int numThreads) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: false)) { await using var sender = new ServiceBusSenderClient( TestEnvironment.ServiceBusConnectionString, scope.QueueName); int numMessages = 50; await sender.SendBatchAsync(GetMessages(numMessages)); await using var processor = new ServiceBusProcessorClient( TestEnvironment.ServiceBusConnectionString, scope.QueueName); int messageProcessedCt = 0; // stop processing halfway through int stopAfterMessagesCt = numMessages / 2; var options = new ProcessingOptions() { MaxConcurrentCalls = numThreads }; TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(options); async Task ProcessMessage(ServiceBusMessage message, ServiceBusSession session) { Interlocked.Increment(ref messageProcessedCt); if (messageProcessedCt == stopAfterMessagesCt) { await processor.StopProcessingAsync(); tcs.TrySetResult(true); } } await tcs.Task; var remainingCt = 0; var receiver = new ServiceBusReceiverClient(TestEnvironment.ServiceBusConnectionString, scope.QueueName); foreach (ServiceBusMessage message in await receiver.ReceiveBatchAsync(numMessages)) { remainingCt++; } // can't assert on the exact amount processed due to threads that // are already in flight when calling StopProcessingAsync, but we can at least verify that there are remaining messages Assert.IsTrue(remainingCt > 0); Assert.IsTrue(messageProcessedCt < numMessages); } }
public async Task Receive_Event(int numThreads) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: false)) { await using var sender = new ServiceBusSenderClient( TestEnvironment.ServiceBusConnectionString, 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 await sender.SendBatchAsync(GetMessages(numThreads * 2)); await using var processor = new ServiceBusProcessorClient( TestEnvironment.ServiceBusConnectionString, scope.QueueName); int messageCt = 0; var options = new ProcessingOptions() { MaxConcurrentCalls = numThreads }; 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(options); async Task ProcessMessage(ServiceBusMessage message, ServiceBusSession session) { await processor.CompleteAsync(message.SystemProperties.LockToken); Interlocked.Increment(ref messageCt); var setIndex = Interlocked.Increment(ref completionSourceIndex); completionSources[setIndex].TrySetResult(true); } await Task.WhenAll(completionSources.Select(source => source.Task)); // we complete each thread after one message being processed, so the total number of messages // processed should equal the number of threads Assert.AreEqual(numThreads, messageCt); } }
public async Task Process_Event_Consumes_All_Messages(int numThreads) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: true)) { await using var sender = new ServiceBusSenderClient( TestEnvironment.ServiceBusConnectionString, 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 clientOptions = new ServiceBusProcessorClientOptions() { IsSessionEntity = true, ReceiveMode = ReceiveMode.ReceiveAndDelete, RetryOptions = new ServiceBusRetryOptions() { // to prevent the receive batch from taking a long time when we // expect it to fail MaximumRetries = 0, TryTimeout = TimeSpan.FromSeconds(5) } }; await using var processor = new ServiceBusProcessorClient( TestEnvironment.ServiceBusConnectionString, scope.QueueName, clientOptions); int messageCt = 0; var options = new ProcessingOptions() { MaxConcurrentCalls = numThreads }; TaskCompletionSource <bool> taskCompletionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(options); async Task ProcessMessage(ServiceBusMessage message, ServiceBusSession session) { await processor.CompleteAsync(message.SystemProperties.LockToken); sessions.TryRemove(message.SessionId, out bool _); Assert.AreEqual(message.SessionId, await session.GetSessionIdAsync()); Assert.IsNotNull(await session.GetLockedUntilUtcAsync()); var ct = Interlocked.Increment(ref messageCt); if (ct == numThreads) { taskCompletionSource.SetResult(true); } } await taskCompletionSource.Task; // we only give each thread enough time to process one message, so the total number of 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); // 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 await using var receiver = new ServiceBusReceiverClient( TestEnvironment.ServiceBusConnectionString, scope.QueueName); Assert.That(async() => await receiver.ReceiveBatchAsync(numThreads), Throws.Exception); } }
public async Task Process_Event_SessionId(int numThreads) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: true)) { await using var sender = new ServiceBusSenderClient( TestEnvironment.ServiceBusConnectionString, 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); } var clientOptions = new ServiceBusProcessorClientOptions() { // just use the last sessionId from the loop above SessionId = sessionId, IsSessionEntity = true, }; await using var processor = new ServiceBusProcessorClient( TestEnvironment.ServiceBusConnectionString, scope.QueueName, clientOptions); int messageCt = 0; var options = new ProcessingOptions() { MaxConcurrentCalls = numThreads }; 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(options); async Task ProcessMessage(ServiceBusMessage message, ServiceBusSession session) { await processor.CompleteAsync(message.SystemProperties.LockToken); Interlocked.Increment(ref messageCt); sessions.TryRemove(message.SessionId, out bool _); Assert.AreEqual(sessionId, message.SessionId); Assert.AreEqual(sessionId, await session.GetSessionIdAsync()); Assert.IsNotNull(await session.GetLockedUntilUtcAsync()); var setIndex = Interlocked.Increment(ref completionSourceIndex); completionSources[setIndex].TrySetResult(true); } await Task.WhenAny(completionSources.Select(source => source.Task)); // although we are allowing concurrent calls, // since we are specifying a specific session, the // concurrency won't really work as only one receiver can be linked to the session; TODO may want to add validation for this Assert.AreEqual(1, messageCt); // we should have received messages from only the specified session Assert.AreEqual(numThreads - 1, sessions.Count); } }
public async Task Process_Event(int numThreads) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: true)) { await using var sender = new ServiceBusSenderClient( TestEnvironment.ServiceBusConnectionString, 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 clientOptions = new ServiceBusProcessorClientOptions() { IsSessionEntity = true, ReceiveMode = ReceiveMode.ReceiveAndDelete }; await using var processor = new ServiceBusProcessorClient( TestEnvironment.ServiceBusConnectionString, scope.QueueName, clientOptions); int messageCt = 0; var options = new ProcessingOptions() { MaxConcurrentCalls = numThreads }; 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(options); async Task ProcessMessage(ServiceBusMessage message, ServiceBusSession session) { await processor.CompleteAsync(message.SystemProperties.LockToken); Interlocked.Increment(ref messageCt); sessions.TryRemove(message.SessionId, out bool _); Assert.AreEqual(message.SessionId, await session.GetSessionIdAsync()); Assert.IsNotNull(await session.GetLockedUntilUtcAsync()); var setIndex = Interlocked.Increment(ref completionSourceIndex); completionSources[setIndex].TrySetResult(true); } await Task.WhenAll(completionSources.Select(source => source.Task)); // we only give each thread enough time to process one message, so the total number of 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); } }