AxonFramework / AxonFramework

Framework for Evolutionary Message-Driven Microservices on the JVM
https://axoniq.io/
Apache License 2.0
3.3k stars 788 forks source link

Consistency gap in conjunction with state-stored aggregates #1103

Open OLibutzki opened 5 years ago

OLibutzki commented 5 years ago

Hello,

as already stated in the Axon forum there is a consistency gap in Axon in conjunction with state-based aggregates.

If an event is published successfully , but the command handler which emitted the event cannot persist its changes (because the database connection is broken or a database constraint is violated), the event is published, although the command has not been processed properly.

In the forum thread I recommended to use the outbox pattern. @abuijze answered that this has been discussed and discarded as it might be hard to implement in an inpropriate way.

Anyway, I open this issue in order to discuss how to deal with the consistency gap. Maybe we can find a solution which closes the gap and fits into the Axon framework quite well. I am keen to support you however and as good as I can.

If you do not target for a solution (as it doesn't fit to your project's aims), feel free to close this issue, otherwise I am happy about a lively discussion.

Kind regards Oliver

smcvb commented 5 years ago

Thanks for filing this issue @OLibutzki, I'll ensure we'll discuss this in our up coming meeting.

OLibutzki commented 5 years ago

Thanks @smcvb, I'm keen to know the results.

OLibutzki commented 5 years ago

Hi @smcvb,

any progress? The issue is still critical for us.

I recently played around with a local event store which represents an outbox. All the events which occur in a module are stored in a local event store (backed by an ACID transaction). A TrackingEventProcessor tracks this event store and propagtes he events to a global/shared event store.

public class LocalEventStoreListener {

    private static final Logger log = LoggerFactory.getLogger( LocalEventStoreListener.class );

    private final EventBus globalEventBus;

    public LocalEventStoreListener(EventBus globalEventBus) {
        this.globalEventBus = globalEventBus;
    }

    @EventHandler
    public void on(EventMessage<?> eventMessage) {
        log.info("Event handled: " + eventMessage);
        globalEventBus.publish(eventMessage);
    }
}

This works quite well, but things become complicated when it comes to event handlers. The primary EventStore is the local one which does not contain the events emitted by other modules. Therefore one has to specify which event bus should be used. As I use Spring Boot I have configured something like this (Axon.eventBus is the bean name of the glocal event bus):

axon.eventhandling.processors[de.libutzki.axon.axonhierarchical.module1].mode=tracking
axon.eventhandling.processors[de.libutzki.axon.axonhierarchical.module1].source=Axon.eventBus

That's ok, but the granularity does not fit my needs as by default an event processor is created (and configured) for each package which contains an EventHandler. I would like to track the event's emitted by my context from the local event bus and the other events from the global event store (omitting the events which are emitted by module as I would handle them twice otherwise).

Currently I explore how Axon deals with processing groups, processors, handlers and their assignments. Honestly, that's not trivial. Maybe you can give me some hints where to look at and/or about the state of your internal discussion.

smcvb commented 5 years ago

Without going into details on the issue, I feel obliged to state the following.

We have not been able to discuss this due to more pressing issue from our part which eat up our time. If you'd want this situation you're dealing with to be picked up quicker, I'd suggest to contact AxonIQ directly. More direct contact with us through those channels will make it so this could be resolved quicker for you.

OLibutzki commented 5 years ago

Ok, thanks for your quick reply. Axon is an open-source framework and it's natural that your objectives and priorities might differ from the ones of some users.

I will evaluate my idea/solution first and might contact you later on.

Keep up the good work!

omallo commented 4 years ago

I'm completely new to Axon but isn't there a similar consistency gap when projecting events to a view model which is persisted in an external database?

I'm just looking at the Axon quick start project and I was wondering how Axon can guarantee that the changes in the database and in the Axon event store are atomic when processing an event as is done e.g. here?

OLibutzki commented 4 years ago

@omallo it is not atomic, but eventual consistent.

The emitting component published the event in the event store and the client side (the event listener) tracks the event store against certain events. It does so by using a cursor (TrackingToken) which indicates which events have already been processed.

omallo commented 4 years ago

@OLibutzki , thanks for your quick feedback.

I had seen that Axon maintains a TOKEN_ENTRY table in the same database in which the view models are persisted but hadn't realised that the token keeps track of the last consumed event since the token is very long and it only changes in a subtle way when new events are consumed.

Like that, it makes perfectly sense that Axon can maintain the required consistency since the token table and the view models are updated in the same transaction. Thanks again for your explanation.