question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Discussion] Service Bus - Track 2 - Processor

See original GitHub issue

The intent of this issue is to solicit feedback from the community in advance of the GA of the next major version of the Service Bus library (which will follow several Preview releases). As a reference, the .NET Azure SDK guidelines can be found here: https://azure.github.io/azure-sdk/dotnet_introduction.html Terminology: Track 0 refers to the WindowsAzure.ServiceBus library Track 1 refers to the Microsoft.Azure.ServiceBus library Track 2 refers to the new library, Azure.Messaging.ServiceBus, we are working on releasing.

Background In the previous versions of the Service Bus library there were two models for receiving messages, which we will refer to here as “Push” and “Pull”. In Track 1, pull was used with the Receive method in the MessageReceiver. Push could be used on the QueueClient/SubscriptionClient/MessageReceiver by calling the RegisterMessageHandler/RegisterSessionHandler methods and passing in callbacks. The push methods provided a few features to make interacting with messages easier:

  • automatically renew message/session locks
  • option to automatically complete a message after the callback finishes executing.
  • option to specify how many concurrent calls to the user callback to use

Processor In the new version of the Service Bus library, we are offering support for both the push and pull modes of receiving messages. However, instead of intermixing these modes on the same type, we offer a separate dedicated type for the push model, called the ServiceBusProcessor. The same set of features for push-based processing that was available within the Track 1 library will be available in Track 2. The pull operations are available on the ServiceBusReceiver.

var client = new ServiceBusClient("connectionString");
ServiceBusReceiver receiver = client.GetReceiver("myQueue");
// I can use the receiver to directly receive messages. I will need to
// handle completing the message, and renewing message locks myself.
IList<ServiceBusReceivedMessage> messages = await receiver.ReceiveBatchAsync(maximumMessageCount: 10);

// available processing options set to their defaults
var options = new ServiceBusProcessorOptions
{
    MaxConcurrentCalls = 1,
    AutoComplete = true,
    MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(5),
    ReceiveMode = ReceiveMode.PeekLock,
    PrefetchCount = 0
};
ServiceBusProcessor processor = client.GetProcessor("myQueue", options);

The model of passing in callbacks to a method, has been amended to setting event handlers on the processor.

processor.ProcessMessageAsync += MessageHandler;
processor.ProcessErrorAsync += ExceptionHandler;

async Task MessageHandler(ProcessMessageEventArgs args)
{
   // if the handler returns without causing an exception to be thrown, the processor will 
   // automatically complete the received message

  // the message is available within the event args
  ServiceBusReceivedMessage message = args.Message;
  // the settlement methods are also available within the event args
  // for instance, if I wanted to deadletter this message for some reason, I would do so like this
  await args.DeadLetterAsync(message);
}

// the exception handler would be triggered if an exception is thrown at any point while receiving 
// and processing a message. This includes if the user message handler throws an exception.
Task ExceptionHandler(ProcessErrorEventArgs eventArgs)
{
  // the error source indicates at which point in the processing the error occurred
  // e.g. Receive/UserCallback/LockRenewal
    string errorSource = eventArgs.ErrorSource;
    Exception ex = eventArgs.Exception;
    string namespace = eventArgs.FullyQualifiedNamespace;
    string entityPath = eventArgs.EntityPath;

    // I can do some application logging with this information
    return Task.CompletedTask;
}

// now that we have registered our event handlers, we can actually start processing
await processor.StartProcessingAsync();

// to stop processing
await processor.StopProcessingAsync();

Session Processor The configuration used for setting up processing for a session entity is very similar to that shown above for non-session entities.

// available processing options set to their defaults
var options = new ServiceBusProcessorOptions
{
  // the default used for MaxConcurrentCalls is different when getting a session processor
    MaxConcurrentCalls = 8,
    AutoComplete = true,
  // this refers to the session lock when used with a session processor, as opposed to a
  // message lock, as in the case for the regular processor.
    MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(5),
    ReceiveMode = ReceiveMode.PeekLock,
    PrefetchCount = 0
};
// we get back a ServiceBusSessionProcessor as opposed to a ServiceBusProcessor
ServiceBusSessionProcessor processor = client.GetSessionProcessor("myQueue", options);
processor.ProcessMessageAsync += MessageHandler;
processor.ProcessErrorAsync += ExceptionHandler;

// note that the args type used for the message handler is different than 
// it is for the regular processor
async Task MessageHandler(ProcessSessionMessageEventArgs args)
{
  // we can perform session operations ...
  var sessionState = Encoding.UTF8.GetBytes("my session state");
  await args.SetSessionStateAsync(sessionState);
 // in addition to the message settlement operations
  await args.DeadLetterAsync(message);
}

// now that we have registered our event handlers, we can actually start processing
await processor.StartProcessingAsync();

// to stop processing
await processor.StopProcessingAsync();

One other difference under the hood is that when using concurrency (MaxConcurrentCalls > 1) with a session processor, we will have separate receiver links for each thread that is processing messages. Doing this allows us to process multiple sessions at the same time as each receiver link would be scoped to a different session. For the regular processor case, we are following the Track 1 pattern of sharing a single receiver link among the multiple threads.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:29 (28 by maintainers)

github_iconTop GitHub Comments

1reaction
JoshLove-msftcommented, Oct 29, 2020

In the new library, StopProcessing will await all ongoing message callbacks. This means they will be allowed to finish no longer how long they take. You will have access to the CancellationToken in the callback event args so that you can cancel early when the processor is being stopped.

If the user callback throws, then the message will not be completed automatically.

1reaction
SeanFeldmancommented, Apr 8, 2020

I’m saying that we are still automatically completing the message in general. If user code throws an exception, we wouldn’t automatically complete, but I wouldn’t expect users to intentionally throw an exception in order to stop a message from being completed. If they were doing this, I would think using AutoComplete = false would be a better fit. An exception should therefore occur only under exceptional circumstances. So my point was that AutoComplete=true will still generally delete messages from the service without the user opting in to this behavior.

This is not aligned with the most real-world scenario. A normal flow would demand message completion if the processing does not fail. Otherwise, If there’s a deviation such as a need to defer or dead-letter, it’s an explicit operation and intentional.

That’s fair. I will do some digging on this. As a hypothetical, if there wasn’t a Track 0 or Track 1, would you still prefer the default being True? And as usual, thanks for the great input!

I am not using a user callback / handler / processor anymore. It’s not providing me with what I need. The model of auto-completion by default and override that if you don’t want to is very convenient until complex scenarios need to be handled. For Azure Functions, it requires the change to the host.json and opt into the model of completing the messages. If this was inconvenient, it would be changed long time ago to set autoComplete to false.

Not definitive one way or the other, but I did a search for issues that contain MessageHandlerOptions - The majority of the issues are setting AutoComplete = false in the code snippets.

Of course. When working on a repro code, you want to send a message and repeat the error rather than keep resending messages - I’d do exactly that. The intent was to find issues that explicitly raised about the default behaviour of the handler options, not scan how many times a code snippet has that property set to false 😄

If you have 10 or 20 customers that say it doesn’t work for them, are you going to change the default and ignore thousands that didn’t raise anything? If this was a bad default, do you think everyone would be silent? Don’t think so.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Monitoring Azure Service Bus
Learn how to use Azure Monitor to view, analyze, and create alerts on metrics from Azure Service Bus.
Read more >
Azure Event Hubs vs Service Bus Comparison
Azure Event Hubs vs Service Bus: We compared these entities to show the difference in capabilities of both Azure messaging platforms.
Read more >
Bus (computing)
In computer architecture, a bus is a communication system that transfers data between components inside a computer, or between computers.
Read more >
IoT and Analytics in Renewable Energy Systems (Volume 2): ...
[10] and [11] have also discussed IoT-based tracking systems. ... [12] proposed an Autonomous Informative Services for Bus Route Map where GPS is...
Read more >
Operating Systems: CPU Scheduling
2 CPU Scheduler. Whenever the CPU becomes idle, it is the job of the CPU Scheduler ( a.k.a. the short-term scheduler ) to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found