[Axon 3-Spring Boot] Transaction Management

1,021 views
Skip to first unread message

Jérôme M.

unread,
Jan 24, 2018, 5:50:14 AM1/24/18
to Axon Framework Users
Hi all,

I have a question about the transaction management when using the default Axon configuration with Spring Boot.

I can see that the SpringTransactionManager is correctly attached to the Command Bus, so far so good.

So now, I have some events handlers on the read side configured by default as Subscribing processors.
For testing purpose, I'm throwing a Runtime exception in one of the Event Handler, expecting the transaction to be rolled back ( no event and no other entities handled on the read model to be persisted).

Instead, I get a message saying that the event handler failed and "Continuing processing with next listener" but the event is correctly persisted to the event store ( and all other event handlers successfully process the event).

Could you please enlighten me about the default strategy ? And how could we fine tune this configuration ?

As far as I remember, in Axon 2, the default behavior was the one described above, but I might be wrong.

Thank you,
Best Regards,
Jerome


 

Steven van Beelen

unread,
Jan 25, 2018, 7:22:04 AM1/25/18
to axonfr...@googlegroups.com
Hi Jerome,

From your description it sounds like you're running an Axon application with the default config.
Thus, the thread publishing the command is also the thread handling the command, applying the event, handling the event in the Event Processor and handling the event in the Event Listener.
By default, an exception in an Event Listener, so the bean which you've annotated with `@EventHandler` functions, will not break the overall execution of your Subscribing Event Processor.

It thus not perform a rollback of the overall UnitOfWork, but only for that listener.
As such your event will be persisted in that scenario.

Additionally, I'd argue it's desirable that an exception in your query side does not block the persisting of the event in your store.
It's not the event storage, your command model, which went wrong in that scenario, it's  you're query model which failed.

If you would however want to adjust the exception handling behavior in your Event Listeners, you should provide `ListenerInvocationErrorHandler`.
If you're in a Spring environment, you can simply add a bean of your own `ListenerInvocationErrorHandler`, which will automatically get picked up by your Event Processors.

Hope this helps you out Jerome!

Cheers,
Steven

--
You received this message because you are subscribed to the Google Groups "Axon Framework Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to axonframewor...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jorg Heymans

unread,
Jun 12, 2018, 9:58:18 AM6/12/18
to Axon Framework Users
Hi,

Is there any other configuration required to achieve a rollback of the eventstore in case of a query model failure ? I have put together a very basic application with this configuration, but the eventstore commits instead of rolling back when an @EventHandler throws an exception.

  @Bean
  AggregateConfigurer<TheAR> configureProcessAR() {
    return AggregateConfigurer.defaultConfiguration(TheAR.class);
  }

  @Bean
  EventStorageEngine eventStorageEngine(DataSource dataSource,
      TransactionManager transactionManager) {
    JdbcEventStorageEngine jdbcEventStorageEngine = new JdbcEventStorageEngine(
        new SpringDataSourceConnectionProvider(dataSource), transactionManager);
    // because the jdbc event storage engine does not create the tables automatically
    jdbcEventStorageEngine.createSchema(HsqlEventTableFactory.INSTANCE);
    return jdbcEventStorageEngine;
  }

  @Bean
  PropagatingErrorHandler propagatingErrorHandler() {
    return PropagatingErrorHandler.instance();
  }

  @Bean
  SimpleCommandBus commandBus(TransactionManager transactionManager) {
    // instantiate the commandbus to add a message monitor
    SimpleCommandBus simpleCommandBus = new SimpleCommandBus(transactionManager, messageMonitor());
    simpleCommandBus
        .registerHandlerInterceptor(new TransactionManagingInterceptor<>(transactionManager));
    simpleCommandBus.setRollbackConfiguration(RollbackConfigurationType.ANY_THROWABLE);
    return simpleCommandBus;
  }

The command is sent transactionally, so i expected all axon components (given above config) to participate in the same transaction and have a global commit or rollback. 

    new TransactionTemplate(platformTransactionManager)
        .execute(new TransactionCallbackWithoutResult() {
          @Override
          protected void doInTransactionWithoutResult(TransactionStatus status) {
            commandGateway.send(command);
          }
        });



Thanks,
Jorg

Jorg Heymans

unread,
Jun 13, 2018, 2:50:24 AM6/13/18
to Axon Framework Users
It seems sendAndWait on the command gateway produces the 'desired' behaviour, which makes sense now that i think about it.

The following would also work, i guess semantically it's the same as doing sendAndWait

    new TransactionTemplate(platformTransactionManager)
        .execute(new TransactionCallbackWithoutResult() {
          @Override
          protected void doInTransactionWithoutResult(TransactionStatus status) {
            FutureCallback<TheCommand, Object> callback = new FutureCallback<>();
            commandGateway.send(command, callback);
            callback.getResult();
          }
        });




Jorg

Steven van Beelen

unread,
Jun 13, 2018, 10:14:13 AM6/13/18
to axonfr...@googlegroups.com
Hi Jorg,

As a heads up I'd like to point out that what you describe here will only work if you've got a Axon-app with default configuration.
As soon as you're using an asynchronous mechanism of handling your events (e.g. the AsynchronousEventProcessingStrategy on the Subscribing event processor or the TrackingEventProcessor), a failure in event handling will not rollback up to the command publishing.

Cheers,
Steven
Reply all
Reply to author
Forward
0 new messages