How to handle commands failed due to event handler exception

Hi,

When using the SimpleCommandBus an exception in an event handler will cause the unit of work to be rolled back. So, in effect, the command was not applied and the event not stored.

However, the exception is not propagated.

In the case of commandGateway.sendAndWait, the return value from the commandHandler is returned.

In the case of commandGateway.send with callback, the onSuccess callback is invoked.

How can we know that the command was not processed?

Somehow it just feels wrong that this exception is treated as success, and propagating the exception (or isolating it) seems more appropriate.

Hey Tim,

Thanks for posting that question. I was about to ask the same thing. I need my controller to send back a proper message for whatever exception happened in the event listener. But all I get is that the transaction was rolled back, which is not the real reason why the command failed in the first place.

Regards,
Bruno

Hi Tim, Bruno,

I’m guessing you’re mostly running on the Axon defaults within your application.

Your issue probably arises because the default EventHandlerInvoker used for you EventProcessors, contains the LoggingErrorHandler.
You could change this to the PropagatingErrorHandler, which would thus propagate the exception you get in the event handlers up.

However, I do like to note that it’s not your command which was not processed in the case you’re describing, it’s the event which failed.
So making your write side fail, or receive an exception from the event handling side is not completely correct.

If your application set up completely relies on the command publishing side to be noted of a failure on the event handling side, then I believe the solution I suggested is correct though.

Hoping this helps!

Cheers,

Steven

Hi Steven,

Thanks for the response, I will play around with the PropagatingErrorHandler right away :slight_smile:

Thanks for your other comments as well. You are right, the command didn’t literally fail, but I see the command (maybe incorrectly) as some sort of wrapper to the controllers request. Meaning, when the command is sent, everything else happens within the context of that command. The aggregate handles the command, applies the event… then the event is handled buy the event listener class which will persist the changes to the SQL database. Finally Axon signals the commit phase and the command execution ends and the controller returns a response. Hopefully something like that… as I haven’t debugged all the code… yet :slight_smile:

So, in the scenario, imagine that we are inserting a duplicate record in the database. It is correctly as you pointed, the event handler that will throw the first exception. On the commit phase, since the command didn’t fail, it will attempt to commit the database transaction and a second exception is thrown: " Transaction was marked for rollback only; cannot commit".

Considering the scenario above, it is a requirement to our application that the events are NOT published and the rabbitMQ messages are NOT sent, since the database write failed. And all that is being handled properly, just need to get the proper message sent to the client. I hope this is what you mentioned to be a correct solution. If not, can you explain why?

Thanks,
Bruno

Hi Everyone,

I feel like my question got a bit side-tracked, but I hope Bruno got his answers (no hard feelings :)).

It did make me question if I expressed myself correctly, and make me think about how to turn this into a small example. It turns out, I wrote down the wrong question myself, let me correct that.

The issue:

The system is using EventStorageEngine (with JDBC) [and not SimpleCommandBus]. (Don’t ask how I got that mixed up)

The good:
Non-db exceptions in the event handlers are treated as expected (Axon isolates them i.e. “Continuing processing with next listener”)

The bad:

DB-exceptions (wrong column name, table missing) in the event handlers seem to trigger a transaction rollback, and the command-handler looks to be operating in the same transaction (or the apply(event) is). So the event is not stored either.

(The log also reports, probably in vain, “Continuing processing with next listener” in this case)

The ugly:

The command-handler reports nothing about the rollback, and to the caller it is indistinguishable from a success.
In the case of commandGateway.sendAndWait, the return value from the commandHandler is returned.
In the case of commandGateway.send with callback, the onSuccess callback is invoked.

Hope this makes the issue more clear.

Let me know if you need me to create a project that showcases this issue.

Cheers,

Tim

Hi Tim,

It sounds like you may be hit with a side effect from using the SimpleEventBus with the default configuration here.

So what happens in the simple set up is that the command comes in at the SimpleCommandBus, gets handled and if successful will return a success.
Using the SimpleEventBus with the default configuration the process continues a bit there and before returning the successful completion of the command will run all the event handlers in the same thread.
If one of the event handlers causes an exception then that’s where things will likely fail.

To work around this, you could decouple the event handlers from the command handler. Look into the async event processing; i.e by using the AsynchronousEventProcessingStrategy. See https://docs.axonframework.org/v/3.0/part3/event-processing.html Alternatively using a distributed event bus (i.e. using RabbitMQ) will get you similar decoupling.
Both solutions do require you to think about how to handle an error in an event handler or risk events being ‘lost’.

HTH,
Thomas