I am trying to publish a message from the catch block in the Rebus message handlers without affecting the retry mechanism of rebus.
My intent is,
I cannot achieve the above because if an exception is thrown from the rebus message handlers, that message is automatically flagged for re-delivery and the whole pipeline transaction is rolled back. This negates the second point above as when transaction is rolled back, the message I sent to be published is rolled back as well. Is there a way I could achieve this i.e. publishing the message as well as auto-retry ability. My message handler code is below.
public Task Handle(SomeMessage message)
{
try
{
//Some code that may result in an error
}
catch (Exception ex)
{
_bus.PublishSomeMessageErrorEvent(ex);
// throw an error and let Rebus retry the delivery.
throw;
}
return Task.CompletedTask;
}
I Also tried working with second level retries so that when failed message comes in the IHandleMessages<IFailed> handler I would simply publish the message with _bus.PublishSomeMessageErrorEvent(...) method but while setting the second level retry I received exception while starting the bus.
_bus = Configure.With(...)
.Options(r=>r.SimpleRetryStrategy(secondLevelRetriesEnabled:true))
.ConfigureSqlServerTransportFromAppConfig()
.Logging(c => c.Log4Net())
.Start();
The exception Attempted to register primary -> Rebus.Retry.Simple.SimpleRetryStrategySettings, but a primary registration already exists: primary -> Rebus.Retry.Simple.SimpleRetryStrategySettings
You can use Rebus' built-in "transaction scope suppressor" by creating a scope with it like this:
using (new RebusTransactionScopeSuppressor())
{
// bus operations in here will not be enlisted
// in the transaction scope of the message
// context (i.e. the one associated with the
// handler)
}
so your message handler can simply go
public class SomeMessageHandler : IHandleMessages<SomeMessage>
{
readonly IBus _bus;
public SomeMessageHandler(IBus bus) => _bus = bus ?? throw new ArgumentNullException(nameof(bus));
public async Task Handle(SomeMessage message)
{
try
{
await DoSomethingThatCanThrow();
}
catch (Exception exception)
{
// publish event
using (new RebusTransactionScopeSuppressor())
{
var evt = new CaughtThisException(exception.ToString());
await _bus.Publish(evt);
}
// rethrow to have Rebus exception handling kick in
throw;
}
}
async Task DoSomethingThatCanThrow()
{
// (...)
}
}
and achieve what you want.
PS: Remember to await
async things 🙂 it's not clear whether the PublishSomeMessageErrorEvent
is sync or async, but somehow your code looks a bit like it could actually be async.