Using Azure.Messaging.ServiceBus
.
I want to send multiple messages to Azure Service Bus in one transaction. I'm fine with the limit of 100 messages per transaction.
What are the pros and cons of these two approaches?
TransactionScope
Multiple single-message send operations in a TransactionScope.
public async Task SendMessages(IEnumerable<ServiceBusMessage> messages, CancellationToken cancellationToken)
{
using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
var tasks = messages.Select(m => _serviceBusSender.SendMessageAsync(m, cancellationToken));
await Task.WhenAll(tasks);
transaction.Complete();
}
ServiceBusMessageBatch
Single send operation, send a ServiceBusMessageBatch.
public async Task SendMessages(IEnumerable<ServiceBusMessage> messages, CancellationToken cancellationToken)
{
var batch = await _serviceBusSender.CreateMessageBatchAsync(cancellationToken);
foreach (var message in messages)
{
if (!batch.TryAddMessage(message))
throw new Exception("...");
}
await _serviceBusSender.SendMessagesAsync(batch, cancellationToken);
}
A batch is a way to send multiple messages to the broker without exceeding the total size constraint as, during batch construction, the API will not add messages that would cause the batch size to exceed the maximum allowed. That's what a batch is for, for safety to ensure you don't get message size exception upon dispatching.
TransactionScope
ensures that messages sent within the same transaction are all sent if successful or reverted if one of the messages fails.
With a batch, it's a single call to the broker. There are multiple concurrent calls to the broker with transaction scope, such as the number of send operations.
The benefit of the batch: a single call to the broker that will have no chance to fail due to too many messages grouped together and exceeding maximum size. This is handy when you have multiple messages ready to be sent.
The benefit of the transaction scope is when you need to dispatch messages but don't have them all available within the same instance or need to send in a fire-and-forget mode but still want to ensure those are sent atomically.