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);
                }
            };
        }
Example #2
0
        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();
        }
Example #3
0
        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);
                }
            }
        }