I have these following models, even though I used select_for_update
in the aggregation, I'm still encountering race condition in this withdraw
method :
How do I lock the entries
until withdraw()
method finished?
class BalanceHandler():
@atomic
def withdraw(account, amount):
# race condition happened here where multiple withdrawals happenning concurrently
# can't use entries.select_for_update().get_total() because it's not available for QuerySet
if account.entries.get_total() < amount:
raise Exception("not enough balance")
# do something with the amount
class LedgerEntry(models.Model):
amount = models.DecimalField(max_digits=18, decimal_places=6)
ledger_account = models.ForeignKey(LedgerAccount, related_name='entries')
class LedgerEntryManager(models.Manager):
def get_total(account, *args, **kwargs):
return self.select_for_update().filter(*args, **kwargs).aggregate(Sum('amount'))['amount__sum']
class LedgerAccount(models.Model):
account_number = models.CharField(max_length=20, editable=False)
Note: I found this question ( How to prevent race condition in Django on INSERT with limiting SUM?) but in that case, OP was able to lock the master object (project). In my case, how do I lock the LedgerAccount object during withdraw()
or get_total()
?