rebus-org / Rebus

:bus: Simple and lean service bus implementation for .NET
https://mookid.dk/category/rebus
Other
2.26k stars 355 forks source link

Easy way of forwarding message to error queue #858

Closed mookid8000 closed 4 years ago

mookid8000 commented 4 years ago

If you want to swiftly forward the message currently being handled to the error queue, you currently need to do manual stuff like this:

await _bus.Advanced.TransportMessage.Forward("error", new Dictionary<string, string>
{
    [Headers.DeferCount] = "0",
    [Headers.ErrorDetails] = exception.ToString(),
    [Headers.SourceQueue] = "my-queue"
});

which is not as pretty as it could have been.

The transport message API needs a ForwardToErrorQueue method that would change the above code to

await _bus.Advanced.TransportMessage.ForwardToErrorQueue();

which is how it should always have been. 😉

rsivanov commented 4 years ago

Doesn't FailFastException exist for exactly that kind of scenario - moving the message to an error queue?

mookid8000 commented 4 years ago

@rsivanov yes it does 🙂 there are a few differences though.

When you throw an exception marked with the IFailFastException interface, the entire message transaction is rolled back. This means that all outgoing messages sent/published at that point will not be sent/published. It also generates quite a bit of noise in the log.

If Rebus had the ability to await _bus.Advanced.TransportMessage.ForwardToErrorQueue(), it would be a normal send operation, and you would have to log it, if you wanted to log something. It would be much more smooth that way.

I think there's a difference. How do you see it?

rsivanov commented 4 years ago

I can see the difference in behavior you're describing, but I'm not sure there should be any. I've just read the page in Rebus wiki about failing fast once again, but haven't found any technicalities like "This means that all outgoing messages sent/published at that point will not be sent/published. It also generates quite a bit of noise in the log.".

The way I see it, there should be two possible outcomes of processing the message:

  1. A success, in which case the message is removed from the original queue.
  2. A failure, which can be a temporary problem and should be retried according to the retry strategy rules, or some unrecoverable problem (business constraints, schema errors, etc.) that requires human intervention and should be resolved later by moving the message to an error queue. In case of a failure, I can't imagine a scenario, when my message is moved to an error queue, but the outgoing messages should be sent anyway. I would expect exactly the same behavior, that you described for IFailFastException, except for maybe generating excessive noise in the log 🙂.

So my point is that in my opinion an API should be as obvious as possible and shouldn't provide two ways to do conceptually the same thing, but with some different implicit side effects, because in that case someone will eventually choose the wrong one.

mookid8000 commented 4 years ago

You might be right 🙂

I also remembered one of the reasons that this API was not added a long time ago – if it was an operation like await _bus.Advanced.TransportMessage.ForwardToErrorQueue(), it will be possible to

await _bus.Advanced.TransportMessage.ForwardToErrorQueue();
await _bus.Advanced.TransportMessage.ForwardToErrorQueue();
await _bus.Advanced.TransportMessage.ForwardToErrorQueue();

and thus end up with three copies of the same message in the dead-letter queue.

mookid8000 commented 4 years ago

Rebus 6.2.1 is available on NuGet.org now.

It has the ability to

await _bus.Advanced.TransportMessage.Deadletter();

which will mark the message currently being handled as one to be dead-lettered.

Whatever "dead-lettering" means depends on the configuration, so by default it will move the message to the error queue. If you're running with Fleet Manager, then the message will be stored there.

It's possible to customize the message passed along with the poison message like this:

await _bus.Advanced.TransportMessage.Deadletter(errorDetails: "Something went horribly wrong!");