Problem
When the Outbox processes a handler or transactional session that produces multiple outgoing messages to the same destination, the dispatcher currently sends each one as a separate, individual broker call (SendMessageAsync). This is wasteful: each call incurs a network round-trip to Azure Service Bus, and cloud latency is non-trivial (see Azure Service Bus performance improvements).
Current behaviour
DispatchIsolatedOperations (the path taken for Outbox-dispatched messages) iterates over all operations and fires one DispatchForDestination task per message, even when multiple messages share the same destination. It does not batch them.
foreach (var operation in operations)
{
// one individual SendMessageAsync call per message
dispatchTasks.Add(DispatchForDestination(destination, isTopic, ..., message, cancellationToken));
}
Meanwhile DispatchBatchedOperations (used for non-isolated operations already grouped per destination) already uses ServiceBusMessageBatch and SendMessagesAsync to batch messages to the same destination.
Desired behaviour
DispatchIsolatedOperations should use the same batch-send path as DispatchBatchedOperations — i.e. call DispatchBatchOrFallbackToIndividualSendsForDestination — so that multiple outgoing messages to the same destination are sent in a single broker call.
The correctness constraint is already satisfied: isolated operations must not be enlisted in the receive/committable transaction. The existing Transaction? noTransaction = null guard already ensures this. Batching is purely a performance optimization and does not affect delivery guarantees.
Context
Multiple isolated dispatches (possible when the outbox is at play with a handler or a transactional session that sends multiple messages) can be batched for efficiency reasons as explained in
// Changing the dispatch consistency to be isolated to make sure the transport doesn't
// enlist the operations in the receive transaction. The transport might still want to batch
// operations for efficiency reasons but should never enlist in the incoming transport transaction.
// Otherwise a failure to ACK the incoming message after Outbox storage has been set to Dispatched
// would result in outgoing message loss.
https://github.com/Particular/NServiceBus/blob/master/src/NServiceBus.Core/Reliability/Outbox/ForceBatchDispatchToBeIsolatedBehavior.cs#L14-L19
Problem
When the Outbox processes a handler or transactional session that produces multiple outgoing messages to the same destination, the dispatcher currently sends each one as a separate, individual broker call (SendMessageAsync). This is wasteful: each call incurs a network round-trip to Azure Service Bus, and cloud latency is non-trivial (see Azure Service Bus performance improvements).
Current behaviour
DispatchIsolatedOperations(the path taken for Outbox-dispatched messages) iterates over all operations and fires oneDispatchForDestinationtask per message, even when multiple messages share the same destination. It does not batch them.Meanwhile
DispatchBatchedOperations(used for non-isolated operations already grouped per destination) already uses ServiceBusMessageBatch and SendMessagesAsync to batch messages to the same destination.Desired behaviour
DispatchIsolatedOperations should use the same batch-send path as
DispatchBatchedOperations— i.e. callDispatchBatchOrFallbackToIndividualSendsForDestination— so that multiple outgoing messages to the same destination are sent in a single broker call.The correctness constraint is already satisfied: isolated operations must not be enlisted in the receive/committable transaction. The existing
Transaction? noTransaction = nullguard already ensures this. Batching is purely a performance optimization and does not affect delivery guarantees.Context
Multiple isolated dispatches (possible when the outbox is at play with a handler or a transactional session that sends multiple messages) can be batched for efficiency reasons as explained in
https://github.com/Particular/NServiceBus/blob/master/src/NServiceBus.Core/Reliability/Outbox/ForceBatchDispatchToBeIsolatedBehavior.cs#L14-L19