Axon 3 RC1 Events resulting from commands send from Saga not stored

Hello,

I am having a bit of trouble moving my code from M4 to RC1. The following scenario:

  • command A creates Aggregate A’ and generates “createdEvent” (which I can find in the event store by manual inspection of the MongoDB)
  • “createdEvent” starts Saga
  • Saga sends command to create Aggregate B. The consolo log states, that the creation command has been executed successfully,
    but no event is stored in the event store and the following step in the saga is not triggered.

I already tested other commands which were triggered manually and the respective events were to be found in the event store.

When I do send the command from outside of the saga, the resulting event is stored and subscribed event listeners are called.

I am a bit stumped where to begin to look.

Is there some new feature behind this behaviour? Any idea where to look for the problem?

Thank you.

Dominic

I have created a minimal test to reproduce the isse. There are just two Aggregates Test and Test2 looking like this:

`

@Slf4j
@Aggregate
public class Test2 {

@AggregateIdentifier
String id;

@CommandHandler
public Test2(CreateTest2Command command) {
log.info(“I was called”);
apply(new Test2CreatedEvent(command.getId()));
}

@EventSourcingHandler
public void on(Test2CreatedEvent event) {
id = event.getId();
}

}

`

The matching Saga:

`

@Saga
@Slf4j
@NoArgsConstructor
public class TestSaga {

@Inject
private CommandGateway gateway;

@StartSaga
@SagaEventHandler(associationProperty = “id”)
public void on(TestCreatedEvent event) {
log.info(“saga started”);
CreateTest2Command c = new CreateTest2Command(IdentifierFactory.getInstance().generateIdentifier());
log.info(“send command: {}”, ReflectionToStringBuilder.toString©);
gateway.send©;
}

@SagaEventHandler(associationProperty = “id”)
public void on(Test2CreatedEvent event) {
log.info(“got event: {}”, ReflectionToStringBuilder.toString(event));

end();
}
}

`

The saga is started as expected, the constructor of Test2 is called, but the event is not persisted and the second event handler of the sager never gets called.

Thanks.

Best regards,
Dominic

Hi Dominic,

while trying to reproduce, everything works fine on my machine.

I was wondering, perhaps the command is failing for some reason? You’re doing a gateway.send(), which returns a CompletableFuture. You could do a gateway.send(…).whenComplete(…). Alternatively, you can pass in a callback in the send method as 2nd parameter.

Hope that helps to find the issue.
Cheers,

Allard

Hello, Allard.

Thanks for the pointer.

`
gateway.send©.whenComplete((result, exception) -> log.info(“Result: {} {}”, result, exception));

`

logs:

`
Result: 1d1738ca-9942-4036-928e-be0e3a735c5e null

`

So the command itself is successful.

I end up with the event for the creation of “Test” but with no event related to “Test2” in the event store. I also used the command bus directly before (as opposed to the gateway), with the same result.

The process is triggered via:
`
gateway.send(new CreateTestCommand(IdentifierFactory.getInstance().generateIdentifier()));

`
From a trivial UI component for testing.

Potentially relevant parts of the configuration. This worked just fine with Axon 3 M4

`

@EnableAxon
@Configuration
public class AxonConfigs {

[…]

@Bean
`
public EventStore eventStore() {
return new EmbeddedEventStore(eventStorageEngine());
}

@Bean
EventBus eventBus() {
return eventStore();
}

@Bean
public ResourceInjector resourceInjector() {
return new SpringResourceInjector();
}

@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix(“AxonEventScheduler-”);
scheduler.setPoolSize(Runtime.getRuntime().availableProcessors());
scheduler.setRemoveOnCancelPolicy(true);
return scheduler;
}

@Bean
public SimpleEventScheduler eventScheduler() {
return new SimpleEventScheduler(taskScheduler().getScheduledExecutor(), eventStore());
}

@Bean
EventStorageEngine eventStorageEngine() {
return new MongoEventStorageEngine(
new DefaultMongoTemplate(mongoClient, database, eventStoreCollection, snapshotsCollection));
}

@Bean
public SagaStore sagaStore() {
return new MongoSagaStore(new org.axonframework.mongo.eventhandling.saga.repository.DefaultMongoTemplate(
mongoClient, database, sagaCollection));
}

[…]
}

Thanks.

Best regards,
Dominic

I drilled down so far to notice, that the problem goes away if I remove the following bean definition from the configuration:

`

@Bean
public SagaStore sagaStore() {
return new MongoSagaStore(new org.axonframework.mongo.eventhandling.saga.repository.DefaultMongoTemplate(
mongoClient, database, sagaCollection));
}

`

So something is going on here and I can’t see an explanantion. I guess removing this makes the system to fall back to an in-memory saga store. How exactly this interferes with the persistence of the events I do not understand.

BTW, I always found it ackward that the classes for event store and saga store are both called DefaultMongoTemplate. You cannot import both in the configuration and you have to use the fully qualified name for one of them as seen in the given example code :slight_smile: But maybe it is not intended to be used this way.

Dominic

My guess would be that the storage throws an exception, causing a command to be rolled back. I expect that these exceptions are logged.
Also note that sending commands using commandgateway.send() gives you a future of the result. Those exceptions aren’t logged, unless you explicitly do so.

Cheers,

Allard

Hello, Allard.

Thanks for the pointer. However, no exceptions were logged. Also:

gateway.send(c).whenComplete((result, exception) -> log.info("Result: {} {}", result, exception));

logs:

Result: 1d1738ca-9942-4036-928e-be0e3a735c5e null

So the command itself is successful.

I attached a small project to replicate the problem. I left the problematic configuration in. It assumes a mongodb to be running locally though.

I watched the webinar today. Thanks for that. Maybe I have to try out the new spring-boot support.

Dominic

problem_replication.zip (10.5 KB)

Hi Dominic,

it took a few debug runs to find it, but the problem was actually quite obvious.

The problem starts with the Saga, where the gateway field isn’t made “transient”. This causes the serialization of the Saga to fail. Since the Saga is handled in a SubscribingProcessor (default), the exception bubbles up. It arrives in the part where the command was sent and reports the exception via the CompletableFuture. Unfortunately, that command is also sent using a send() method, without checking the result.

If you use send(command, LoggingCallback.INSTANCE) instead (or do .whenComplete() on the returned future), you will see the exception being logged.

Cheers,

Allard

Thanks a lot, Allard. Sometimes it is the stupid little things. Sorry to bother you with a mistake on my side.

I was not aware of the fact that such errors are only logged if you explicitly ask for it. This is in deed good to know how to do it in such a simple way.

Dominic