When running Axon locally are aggregates cached by default?

Hello,

I have a scenario where I have two nodes accepting requests and then sending commands to an aggregate, I am not running anything in a distributed fashion, so each node is un-aware of the other. In such a scenario, what caching takes place by default? I am seeing a scenario where node1 handles some requests that produce commands for my aggregate, then node2 will handle other requests for the same aggregate, but the state of the aggregate on node2 is not consistent with all of the events in my eventstore. My question is should I expect my aggregate to load all events when processing a command, or is my aggregate being cached so as not to reload events that have been persisted after an aggregate has been loaded on a node?

-Ben

Additional details…

I am running Axon 3.1 using MongoDb as the eventStore

I am also trying to use this framework and not an expert.
What I see from the running log, I can see that for every commands, there is a SQL to get all events of this aggregate, and all handler methods are called. And then THAT command handler and event handler is called. From this log, I think there is no cache. But, maybe there is some strategy about the cache to be enabled. So I didn’t see that in my test.

But I think your problem is, if you check ‘domain_event_entry’ table, which is used to save all events, you can see there a field sequence_number. If you run 2 instance, without any distributed setting, this number maybe corrupted by each other.

在 2018年6月6日星期三 UTC+8上午4:42:54,ben.r…@gmail.com写道:

Hi Ben, Mavlarn,

Assuming we’re in a Axon+Spring context, and we’re set up for event sourcing by having an EventBus which is an EventStore, the behavior is as follows: if Axon detects an @Aggregate class, it knows that a repository is needed for that aggregate. If will check whether one already exists (for a class named MyAggregate, this would by default be a bean called myAggregateRepository, but the name can be overridden). If that bean doesn’t exist, it will set up an EventSourcingRepository. This doesn’t do any caching, so this explains the behaviour that Mavlarn is correctly observing.

If you do want/need caching, the way to do this is to configure a CachingEventSourcingRepository bean for the aggregate explicitly.

@Mavlarn - With or without caching, there’s no risk of aggregate corruption when concurrent access to the same aggregate happens. It’s the job of the event store implementation (RDBMS, Mongo, AxonDB) to ensure uniqueness of the aggregateId + sequencenumber combination. On RDBMS and Mongo event stores, this is configured using indices. In AxonDB, it’s a built-in feature (which of course also uses an index). So if a cache holds a version of an aggregate that is older than what is in the event store, writing the events to the event store will fail and command processing will fail with a transient exception, and the cache entry will be invalidated. Retrying the command will cause a read of the current state and correct processing. While this obviously introduces some inefficiency, it doesn’t lead to corruption.

@Ben - so while your setup will function, it may be inefficient if there’s a bunch of incoming requests triggering commands to the same aggregate being load balanced across the two nodes. Distributing the command bus between the nodes would solve that, it would allow Axon to consistently handle commands targeting the same aggregate instance on the same node, regardless of the node that was processing the incoming external request.

Regards,

Frans,

So when running locally sometimes it appears that event handlers which modify the state of my aggregate are not always processed before event handlers that are applying business logic and producing commands that are being sent back to the same aggregate. The situation I am seeing is that when the aggregate goes to handle the new commands, the state of the aggregate is as if the initial event did not happen, so our logic ends up refusing the command. Here is the timeline of what is happening…

Application sends CommandA on a simpleCommandBus

Aggregate1 handles a CommandA which produces EventA

Aggregate1 has an EventHandler which handles EventA

A Projection has an EventHandler which handles EventA

A business logic “Listener” has an EventHandler which handles EventA and then produces CommandB to be handled by Aggregate1

When Aggregate1 receives CommandB, the state of Aggregate1 does not include EventA, so the Aggregate rejects CommandB

-Ben

Hi Ben,

That really shouldn’t happen in general. When you call AggregateLifecycle.apply in Aggregate1 when handling CommandA, to produce EventA, the event should be handled first by the aggregate’s eventsourcinghandler before it ends up in the bus. This behavior is hard coded in Axon’s AnnotatedAggregate#publish method.

My guess is that there’s something quite specific to your set up that’s causing this. I’ll happily have a look, perhaps we could set up a short screen share to discuss - just let me know.

Kind regards,

Hi Frans,

I wanted to provide you with an update on where we are at with this, it is not resolved. We believe it has to do with how we are connecting to Mongo and the read and write concerns being setup properly. We are running mongo 3.6 and have set up our connection using the following client options…

MongoClientOptions.builder().writeConcern(WriteConcern.MAJORITY).readConcern(ReadConcern.MAJORITY)

But this still does not work.  We are wondering now if our best choice is to use a Postgres database for eventstorage as we can provision one at AWS very quickly.  But now the challenge is finding the right Axon/Spring-Data configuration combination to when setting up the JdbcEventStorageEngine to work 
with postgres. Would you happen to have any good examples of this?

I have to say that the lack of documentation for setting up Axon is giving our team second thoughts about using this framework.  We really like the pattern, but if we cannot get it running without lots of digging for examples we can understand, it is not providing us a lift.  

-Ben

Hi Ben,

Sorry to hear this is causing so much trouble. To help you, I just checked in a small demo project: https://github.com/fransvanbuul/axon-dbconfig-demo

It plays the exact scenario you described (CommandA to Aggregate1, producting EventA, triggering CommandB), and also includes a dummy Saga to test that aspect. Using Spring profiles, it can be configured to run in 4 ways:

  • H2+JPA (which is what you get if you use autoconfigure, have JPA+H2 on the classpath, and do nothing else)
  • Mongo (using Mongo 3.6 like you)
  • Postgres, via JPA (tested on 9.6)
  • Postgres, via JDBC (tested on 9.6 as well)
    Hope this will help you. You might also want to check out https://github.com/AxonIQ/giftcard-demo-series, this has some more configs (including dual postgres, postgres + AxonDB, and a distributed one using AxonHub and AxonDB connected via AxonHub).

Regarding your feedback on the docs: we are aware that there is some room for improvement there. The team has been and still is working very hard on pushing out new functionality, currently finalizing 3.3 which will have some really great new features, like subscription queries. After that, the docs will get some more love.

Meanwhile, to reduce time wasted on figuring out stuff by yourself, there are several other options that might be relevant: we have a training in the US schedule on 11+12 September http://exploreddd.com/workshops/event-driven-microservices-with-axon-framework.html, we can do in-house training, establish a formal support contract (which often takes the form of setting up a dedicated Slack support channel) etc.

Kind regards,

Frans,

Thanks for the examples, they were very helpful.

-Ben