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); } }
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); } }
internal static ServiceBusReceiverClientOptions ToServiceBusReceiverClientOptions(this ServiceBusProcessorClientOptions options) { return(new ServiceBusReceiverClientOptions { ConnectionOptions = options.ConnectionOptions.Clone(), RetryOptions = options.RetryOptions.Clone(), ReceiveMode = options.ReceiveMode, IsSessionEntity = options.IsSessionEntity, SessionId = options.SessionId }); }