0

I am writing an ACME worker service that uses an asynchronous request-reply communication model. In Azure Service Bus I have a queue acme-requests for all inbound requests to the ACME service. I also have a queue acme-replies for all inbound replies to requests that the ACME service has made to other services and their respective request queues.

For example, when the ACME service sends out a request to a Foo service, it would generate a GUID for this request-reply message session and store it in its local database. It then sends a message to the queue foo-requests. The Foo service would respond to the queue acme-replies later.

In the ACME service, I have a message inbox BackgroundService receiving messages from acme-replies and dispatching them to my message handlers. More precisely, in theBackgroundService's process loop I would like to call ServiceBusSessionReceiver replyReceiver = await client.AcceptNextSessionAsync("acme-replies"), process the message and then close the current message session. However, that seems to go against the official recommendation:

The Service Bus objects that interact with the service, such as ServiceBusClient, ServiceBusSender, ServiceBusReceiver, and ServiceBusProcessor, should be registered for dependency injection as singletons (or instantiated once and shared). [...] We recommend that you don't close or dispose these objects after sending or receiving each message.

(Note that ServiceBusSessionReceiver is a subclass of ServiceBusReceiver.)

My questions now is how to combine request-reply, message sessions and dependency injection?

In particular I have the following questions:

  1. How do I handle the lifetimes of ServiceBusSessionReceiver and message session? Their lifetimes seem to be identical.

  2. In consequence, should a ServiceBusSessionReceiver instance really be a singleton or rather a scoped service?

  3. Can I even create a ServiceBusSessionReceiver instance without making it contact the Service Bus immediatly? There is only a method ServiceBusClient.AcceptSessionAsync but no method ServiceBusClient.CreateSessionReceiver.

Joerg
  • 790
  • 2
  • 10
  • 23

1 Answers1

1

The ServiceBusSessionReceiver is somewhat unique in that it is usually short-lived as applications cycle between sessions. The reason is exactly what you've pointed out - that the session receiver and session itself have the same lifetime. You cannot create a ServiceBusSessionReceiver without binding it to an actual session via service operation.

If your application is going to continually read from a well-known session, registering the session receiver as a singleton makes sense. However, that's less common than the pattern of "read from a session until empty and then move to a new session."

In this scenario, your best bet is likely to register the ServiceBusClient as a singleton with DI. Create a ServiceBusSessionReceiver when requesting a session and pass it around as an instance, as needed, until you're done with the session.

I'll reach out to the MS Docs team to see if we can't get that content adjusted. In my opinion, it's misleading as written.

Jesse Squire
  • 6,107
  • 1
  • 27
  • 30
  • Very helpful answer. Thank you. Regarding the scenario `If your application is going to continually read from a well-known session, registering the session receiver as a singleton makes sense.`, how would I do that? Like I mentioned, there is no method `ServiceBusClient.CreateSessionReceiver`. Any advice? – Joerg Mar 01 '23 at 16:29
  • Looks like there are no extensions for the subclients, for some reason. Right now, you'd have to add a custom factory to do it - which I don't like. A basic example is here: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/extensions/Microsoft.Extensions.Azure#registering-a-custom-client-factory. More detail can be found here: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/extensions/Microsoft.Extensions.Azure/tests/AzureClientFactoryTests.cs#L454 – Jesse Squire Mar 01 '23 at 16:33
  • I don't know why we chose not to include subclients, but I'll start discussions and see about getting them added. That's likely to take a bit, though, as public API changes have an approval process to navigate. – Jesse Squire Mar 01 '23 at 16:35
  • I settled on a solution without message sessions for now, which uses the `ReplyTo` and `CorrelationId` message properties for matching the request-reply message pair. Would you mind having a look at [my solution](https://stackoverflow.com/a/75615489/3760986) to the analogous problem on the sender side and comment on it, in case you spot any mistake or bad practice there? I would highly appreciate that. – Joerg Mar 02 '23 at 12:49