How do you handle concurrent changes in a CommandHandler

I’m trying to figure out how to prevent (or deal with) concurrent modification issues.

Lert’s say I have a the following Aggregate:

// Kotlin
@Aggregate  
class WidgetAggregate {  
    @AggregateIdentifier  
    var id: UUID 
    @AggregateMember  
    var name: String
    @AggregateMember  
    var displayName: String 
}

I want to deal with the following use case.

  1. The aggregate is at version 5 (i.e. AggregateLifecycle.getVersion()) returns 5).
    1. name = “Name Five”
    2. displayName = “Display Name Five”
  2. Simultaneously (or at least nearly simultaneously) User A changes the name to “Name Six A” and User B changes the displayName to “Display Name Six B”
  3. Both result in an UpdateWidgetCommand
data class UpdateWidgetCommand(val id: UUID, val name: String, val displayName: String)

Let’s assume the Command for User A hits first. When the Command for User B hits, we (may) want to reject the Command since it is operating on outdated data compared to when the user input the change. For the rejection, in some use cases we only want to reject if the two commands are changing the same property. And in other use case we want to reject if the update is modifying any property if the Aggregate is not identical to the one the user “saw” at the time of the change.

The documentation seems to indicate that a ConflictResolver can be used in the @CommandHanlder to deal with this situation. But that documentation is, I am sorry to say, rather lacking. There is a Note that states:

Expected Aggregate Version

The ConflictResolver will only contain any potentially conflicting events if the Aggregate was loaded with an expected version. Use @TargetAggregateVersion on a field of a command to indicate the expected version of the Aggregate.

But @Steven_van_Beelen stated in this comment in another post:

What the version returns depends on whether you’re Event Sourcing your Aggregate. … In short, as you’re event sourcing the Aggregate, you don’t have to use the @AggregateVersion annotation.

Question 1
Does that also mean the @TargetAggregateVersion also does not apply if we are using Event Sourcing (which we are)?

Question 2
If we are using Event Sourcing, does that mean that the ConflictResolver doesn’t apply and can’t be used? If it does still apply, how do we use it? I’ve tried, but can’t get it to work. The conflictResolver.detectConflicts() lambda Predicate code never runs. If the ConflictResolver does apply to Event Sourcing, can you provide a detailed example of a @CommandHandler that uses a ConflictResolver to detect the conflict in the version, and how to determine what properties have changed.

Question 3
If the ConflictResolver doesn’t apply, how do we deal with this situation?

Question 4
If the Command from User A hits first, and apply an Event, and then the Command hits for user B and apply an Event, is the Event from User A guaranteed to be processed by the @EventSooureHandler and the @EventHandler in a Projection before the Event from User B?

Question 5
Based on the above answers, what is the best way to Unit Test our Aggregate’s Command Handler for this concurrent modification use case using the Axon Test Framework?

Thanks.

Hi Mark,

Concurrent changes are not allowed in a CommandHandler (really the aggregate as a whole) - in your example processing Command for User B will always see the changes from the aggregate having processed Command for User A first.

This also means that the answer to question 4 is Yes.

/Marc

Thanks for the answer. But I think there is a bit of a misunderstanding of my question.

So I understand the framework itself doesn’t allow the concurrent changes. But to the users, there is a concurrent change occurring. For example,

  1. Two users are viewing the data in a web UI where the name property is foo
  2. At the same time, User A submits a request to change the name to bar and User B changes the name to baz.
  3. The Command for User A comes in first and makes the updated of foo -> bar.
  4. When the Command for User B comes in, the effective change will be bar -> baz. But this was not the user’s intent. They wanted to change foo -> baz. If they had known that the value was bar, they may not want to make the change to baz. They may be fine leaving it as bar, or they may want to change it to something completely different. So (in some use cases) we basically want to reject the Command for User B, since it is not updating the value they intended to update, and inform them that they need to refresh the data and resubmit if desired.

How can we handle that use case?

Include the “expected” name value in the command and perform a check in the command handler:

  • User B issues the following Command:

Command {
expectedName: “foo”,
changeTo: “baz”
}

  • In the CommandHandler (after User A’s change):

currentName: “bar” // current aggregate state.
expectedName: “foo”
changeTo: “baz”

You now have all the information you need to make the “proper” decision, such as rejecting User B’s command.