To demonstrate common concurrency issues we are going to work on a bank account model:
id = models.AutoField(
user = models.ForeignKey(
balance = models.IntegerField(
To get started we are going to implement a naive depositand withdrawmethods for an account instance:
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
if amount > self.balance:
self.balance -= amount
This seems innocent enough and it might even pass unit tests and integration tests on localhost. But, what happens when two users perform actions on the same account at the same time?
- User A fetches the account — balance is 100$.
- User B fetches the account — balance is 100$.
- User B withdraws 30$ — balance is updated to 100$ — 30$ = 70$.
- User A deposits 50$ — balance is updated to 100$ + 50$ = 150$.
What happened here?
User B asked to withdraw 30$ and user A deposited 50$ — we expect the balance to be 120$, but we ended up with 150$.
Why did it happen?
At step 4, when user A updated the balance, the amount he had stored in memory was stale (user B had already withdrawn 30$).
To prevent this situation from happening we need to make sure the resource we are working on is not altered while we are working on it.