Search code examples
rebusrebus-rabbitmq

Event Published from within a Command Handler


Simple set up of Rebus with Rabbitmq. From within the Command handler in Consumer Window service I am Publishing an event ( at very beginning of the command handler and then I am doing the actual processing). Eventhandler for the event is also within the Consumer window service. I noticed that although the event was published at the very beginning, its event handler gets executed only when the command has successfully processed.

In a way is makes sense that unless the command (parent) is successful, event (child) should not processed.

However considering a command as a workflow I need to publish event at regular stages (about the completion of a particular state). What is the suggested pattern in Rebus to achieve same?


Solution

  • As you have correctly observed, Rebus collects outbound messages sent from within a message handler, ensuring that they're sent AFTER your handler has finished executing.

    The reason is, that this is almost always what you want – because otherwise events could be published and thus handled by other services, even before your own unit of work was properly committed(*).

    However, if you really want to, you can "escape" Rebus' collection of outgoing messages by temporarily removing the current transaction context like this:

    var transactionContext = AmbientTransactionContext.Current;
    AmbientTransactionContext.Current = null;
    try
    {
        // current transaction will never know....
        await bus.Publish(whee);
    }
    finally
    {
        AmbientTransactionContext.Current = transactionContext;
    }
    

    If you decide to use this method of escaping the transaction context, I suggest you wrap it in something that implements IDisposable, so your code could look like this:

    using(new RebusTransactionContextDismantler())
    {
        // current transaction will never know....
        await bus.Publish(whee);
    }
    

    (*) You could imagine a situation where an event postulates FinalPaymentReceived, but when a subscriber handles it, the order has not been updated accordingly in the database, because the SQL transaction has not been fully committed yet.

    You could also imagine that the SQL transaction could not be committed due to a unique key constraint violation, which may be caused by parallel work being made on some particular object, in which case the SQL transaction is rolled back. It would be pretty disastrous, if events had already been published (and possibly handled!) at that point in time.