Skip to content

[Feature] Azure Service Bus: the producer creates and closes a ServiceBusSender for every message #4486

Open

Description

@gitmln

Is your feature request related to a problem? Please describe.

Sending to Azure Service Bus works reliably; this is about what each send costs. This is not exactly a bug report, it is more like an improvement/feature request.

AzureServiceBusMessageProducer.SendWithDelayAsync gets its sender from GetSenderAsync, where ServiceBusSenderProvider.Get returns new ServiceBusSenderWrapper(_client.CreateSender(topicOrQueueName)), and closes it in finally. So each message opens a new AMQP send link, authorizes it and closes it again. RequeueAsync on the consumer sends through the same method, and the bulk path (CreateBatchesAsync and SendAsync(IAmAMessageBatch)) creates and closes a sender in each call too.

The Azure SDK README suggests keeping senders for regular traffic:

The ServiceBusClient, senders, receivers, and processors are safe to cache and use as a singleton for the lifetime of the application, which is best practice when messages are being sent or received regularly.

(Client lifetime)

The extra round trips show up in the send latency. The events of the SDK's Azure-Messaging-ServiceBus event source, counted for 100 sequential PostAsync calls with 1 KB messages to a queue on the Service Bus emulator:

Event Brighter PostAsync cached ServiceBusSender
SendMessageStart 100 100
ClientCreateStartCore 100 0
CreateSendLinkStart 100 0
RequestAuthorizationStart 100 0
CloseLinkStart 199 0
ClientCloseStartCore 100 0

On the emulator, sequential PostAsync calls ran at about 30 messages per second, against 45–130 with a cached sender, and took 40–50 ms at the median, against 15–18 ms. The emulator's absolute numbers say little about Azure, but the number of operations per message does not depend on the broker.

Describe the solution you'd like

Keeping one sender per entity for the life of the producer, for example by caching senders by entity name and closing them in DisposeAsync, which today completes without closing anything. The README's thread-safety section says that all client instance methods are thread-safe, so concurrent sends could share one sender.

Describe alternatives you've considered

Changing this from outside does not look practical: IServiceBusClientProvider supplies only the client, and the sender is created from it on every call.

Additional context

Related: #3694 (sending with ServiceBusMessageBatch), which would also benefit from a sender that stays open.


Analysis and the repro were prepared with help of an AI assistant.

Activity

  1. iancooper commented on Oct 1, 2026

    @iancooper
    Member

    Thanks @gitmln

  2. changed the title [-]Azure Service Bus: the producer creates and closes a ServiceBusSender for every message[/-] [+][Feature] Azure Service Bus: the producer creates and closes a ServiceBusSender for every message[/+] on Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions