How to maintain consistency when using Event Sourcing? We are using Event Sourcing to work with balance, so we don’t store current state in database. I have following scenario:
- Lets assume I have balance 100$
- I send two events to withdraw 100$, we are assuming that they happened simultaneously, so when they replayed events to check if there is enough money to withdraw 100$, they got positive response.
- Send event to add 100$ to balance
- I end up with 0 balance, but at some point I was able to withdraw more then I have and to have negative balance.
How can I prevent this? Since we don’t use any table to store current state, how can we apply lock?
Addition:
I use Axon framework for event sourcing, maybe someone knows if there is some built in mechanism for this case and knows how it works?
1
How can I prevent this? Since we don’t use any table to store current state, how can we apply lock?
The usual answer in CQRS/ES is that you are “locking” the history of changes (aka the event stream) when you try to extend it with new information.
So the flow of code for your example would usually look something like
- Lock the history
- Compute the balance from a locked copy of the history
- Compare the withdrawal to the balance
- if the withdrawal is permitted:
- add new events to the history
- save the changes to the history
- release the lock
A variation you will sometimes see:
- Fetch an UNLOCKED copy of the history
- Compute the balance from the unlocked copy of the history
- Compare the withdrawal to the balance
- if the withdrawal is permitted:
- add new events to the history
- compare and swap the new history into place of the old
This sort of implementation normally comes with a retry loop, so that a second thread that loses the “compare and swap” race gets another chance to process its message.
1