Skip to content

Thread safety and instance policies

Mogens Heller Grabe edited this page Apr 12, 2023 · 3 revisions

Rebus is generally structured in a way so that you - as a developer of applications that send/publish and handle messages - do NOT have to worry about thread safety and race conditions etc.

On this page, you can find a few words about how this is implemented in the different areas of Rebus.

The bus instance

The bus instance (i.e. whatever sits behind the IBus reference passed to you as a result of calling the entire Configure.With(...) spell, which is the same instance as the one you can resolve from your IoC container if you use one of those) is fully reentrant and can safely be called from multiple threads.

Unless your bus is a one-way client, the bus instance will "host" a number of worker threads, and the bus instance will be the root of this object graph.

As such, the bus instance is often best kept as a SINGLETON in your IoC container, or as a static instance somewhere in your application.

If you have more than one input queue (i.e. you're hosting multiple endpoints) in the same process, you will of course have a bus instance per endpoint - but you will probably never have multiple bus instances per input queue in the same process, because that kind of concurrency would be better achieved by configuring a higher number of threads and/or level of parallelism.

Disposal

When you create a bus instance using the Configure.With(...) syntax, you will most likely be using an IoC container via one of the adapters provided by Rebus.

If you're using Microsoft's hosting model, you'll most likely want to use the Rebus.ServiceProvider package and initiate the configuration by calling services.AddRebus(...);

The configuration API will insert the bus instance (an instance of a class that implements IBus) into the container as a singleton, and it will do so in a way that ensures that the bus is disposed when the container is disposed. This means that you should be sure to dispose the container when your application exists.

If you prefer, you CAN also just go ahead and dispose the bus instance when your application exits - nothing bad will happen if it was somehow disposed more than once.

Message handlers (stateless handlers)

When a message is received, it is dispatched to your message handler(s).

Since the IHandleMessages<TMessage> does not imply any kind of statefulness, you can reuse handler instances if you like. It is advised though, to configure your IoC container to create new handler instances each time they are resolved, in order to avoid mixing them up with SAGA HANDLERS (whose instances must never be reused!)

With some containers (e.g. Castle Windsor and others) this is called "a transient lifestyle".

Saga handlers (stateful handlers)

A saga handler is a class that - in addition to implementing one or more IHandleMessages<TMessage> interfaces - is derived from Saga<TSagaData>, which DOES carry with it some state (that is the entire purpose of a saga - to bring some statefulness into play).

It is absolutely imperative that your saga handlers are configured with a transient lifestyle (i.e. new instances will be created each time the container is asked to resolve), because otherwise the saga data instance on the Data property of the saga handler will suddenly no longer be used by a single thread only.

Clone this wiki locally