public async Task SessionProcessorActivities() { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: true)) { using var listener = new TestDiagnosticListener(EntityScopeFactory.DiagnosticNamespace); var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); ServiceBusSender sender = client.CreateSender(scope.QueueName); var messageCt = 2; var msgs = GetMessages(messageCt, "sessionId"); await sender.SendMessagesAsync(msgs); Activity[] sendActivities = AssertSendActivities(false, sender, msgs, listener); ServiceBusSessionProcessor processor = client.CreateSessionProcessor(scope.QueueName, new ServiceBusSessionProcessorOptions { AutoComplete = false, MaxReceiveWaitTime = TimeSpan.FromSeconds(10), MaxConcurrentSessions = 1 }); TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool>(); int processedMsgCt = 0; processor.ProcessMessageAsync += args => { processedMsgCt++; if (processedMsgCt == messageCt) { tcs.SetResult(true); } return(Task.CompletedTask); }; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(); await tcs.Task; await processor.StopProcessingAsync(); for (int i = 0; i < messageCt; i++) { (string Key, object Value, DiagnosticListener)receiveStart = listener.Events.Dequeue(); (string Key, object Value, DiagnosticListener)receiveStop = listener.Events.Dequeue(); (string Key, object Value, DiagnosticListener)processStart = listener.Events.Dequeue(); Assert.AreEqual(DiagnosticProperty.ProcessSessionMessageActivityName + ".Start", processStart.Key); Activity processActivity = (Activity)processStart.Value; AssertCommonTags(processActivity, processor.EntityPath, processor.FullyQualifiedNamespace); CollectionAssert.Contains( processActivity.Tags, new KeyValuePair <string, string>( DiagnosticProperty.MessageIdAttribute, msgs[i].MessageId)); CollectionAssert.Contains( processActivity.Tags, new KeyValuePair <string, string>( DiagnosticProperty.SessionIdAttribute, msgs[i].SessionId)); (string Key, object Value, DiagnosticListener)processStop = listener.Events.Dequeue(); Assert.AreEqual(DiagnosticProperty.ProcessSessionMessageActivityName + ".Stop", processStop.Key); } }; }
public static async Task RunSessionProcessor(string connectionString, string queueName, TimeSpan timeSpan) { // since ServiceBusClient implements IAsyncDisposable we create it with "await using" await using var client = new ServiceBusClient(connectionString); // get the options to use for configuring the processor var options = new ServiceBusSessionProcessorOptions { // 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. AutoCompleteMessages = false, // I can also allow for processing multiple sessions MaxConcurrentSessions = 5, // By default, there will be a single concurrent call per session. I can // increase that here to enable parallel processing within each session. MaxConcurrentCallsPerSession = 2 }; // create a processor that we can use to process the messages ServiceBusSessionProcessor processor = client.CreateSessionProcessor(queueName, options); processor.ProcessMessageAsync += MessageHandler; processor.ProcessErrorAsync += ErrorHandler; await processor.StartProcessingAsync(); // since the message handler will run in a background thread, in order to prevent // this sample from terminating immediately DateTime endProcessing = DateTime.Now.Add(timeSpan); while (DateTime.Now < endProcessing) { await Task.Delay(100); } // stop processing once the task completion source was completed. await processor.StopProcessingAsync(); }
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.CreateMessageBatchAsync(); messageBatch.TryAddMessage( new ServiceBusMessage(Encoding.UTF8.GetBytes("First")) { SessionId = "Session1" }); messageBatch.TryAddMessage( new ServiceBusMessage(Encoding.UTF8.GetBytes("Second")) { SessionId = "Session2" }); // send the message batch await sender.SendMessagesAsync(messageBatch); // get the options to use for configuring the processor var options = new ServiceBusSessionProcessorOptions { // 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. AutoCompleteMessages = false, // I can also allow for processing multiple sessions MaxConcurrentSessions = 5, // By default, there will be a single concurrent call per session. I can // increase that here to enable parallel processing within each session. MaxConcurrentCallsPerSession = 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); int processedMessageCount = 0; processor.ProcessMessageAsync += MessageHandler; processor.ProcessErrorAsync += ErrorHandler; async Task MessageHandler(ProcessSessionMessageEventArgs args) { var body = args.Message.Body.ToString(); // we can evaluate application logic and use that to determine how to settle the message. await args.CompleteMessageAsync(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(new BinaryData("some state")); // Once we've received the last message, complete the // task completion source. if (Interlocked.Increment(ref processedMessageCount) == 2) { 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 SessionProcessorActivities() { ClientDiagnosticListener.ProducedLink[] messageActivities = null; int messageProcessedCt = 0; bool callbackExecuted = false; _listener = new ClientDiagnosticListener( EntityScopeFactory.DiagnosticNamespace, scopeStartCallback: scope => { if (scope.Name == DiagnosticProperty.ProcessSessionMessageActivityName) { Assert.IsNotNull(messageActivities); Assert.AreEqual( messageActivities[messageProcessedCt], scope.Links.Single()); callbackExecuted = true; } }); await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: true)) { var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); ServiceBusSender sender = client.CreateSender(scope.QueueName); var messageCt = 2; var msgs = ServiceBusTestUtilities.GetMessages(messageCt, "sessionId"); await sender.SendMessagesAsync(msgs); Activity[] sendActivities = AssertSendActivities(false, sender, msgs); messageActivities = sendActivities.Select(a => new ClientDiagnosticListener.ProducedLink(a.ParentId, a.TraceStateString)).ToArray(); ServiceBusSessionProcessor processor = client.CreateSessionProcessor(scope.QueueName, new ServiceBusSessionProcessorOptions { AutoCompleteMessages = false, SessionIdleTimeout = TimeSpan.FromSeconds(10), MaxConcurrentSessions = 1 }); TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool>(); processor.ProcessMessageAsync += args => { if (++messageProcessedCt == messageCt) { tcs.SetResult(true); } return(Task.CompletedTask); }; processor.ProcessErrorAsync += ServiceBusTestUtilities.ExceptionHandler; await processor.StartProcessingAsync(); await tcs.Task; await processor.StopProcessingAsync(); for (int i = 0; i < messageCt; i++) { _listener.AssertAndRemoveScope(DiagnosticProperty.ReceiveActivityName); var processScope = _listener.AssertAndRemoveScope(DiagnosticProperty.ProcessSessionMessageActivityName); AssertCommonTags(processScope.Activity, processor.EntityPath, processor.FullyQualifiedNamespace); } Assert.IsTrue(callbackExecuted); } }
public async Task ProcessEventConsumesAllMessages(int numThreads, bool autoComplete) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: true)) { await using var client = GetClient(); 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 }; ServiceBusSessionProcessor processor = GetNoRetryClient().GetSessionProcessor(scope.QueueName, options); processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(); async Task ProcessMessage(ProcessSessionMessageEventArgs args) { try { var message = args.Message; if (!autoComplete) { await args.CompleteAsync(message); } sessions.TryRemove(message.SessionId, out bool _); Assert.AreEqual(message.SessionId, args.SessionId); Assert.IsNotNull(args.SessionLockedUntil); } 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); } } }