Select for update through a foreign key

36 Views Asked by At

models

class Transaction(models.Model):
    STATUSES = (
        (FAILED_STATUS, "Failed"),
        (DONE_STATUS, "Done"),
        (PENDING_STATUS, "Pending"),
        (BANK_ACCEPTANCE, "Bank Acceptance"),
    )
    amount = models.BigIntegerField()
    status = models.CharField(max_length=20, default=PENDING_STATUS, choices=STATUSES)
    wallet = models.ForeignKey("Wallet", on_delete=models.PROTECT)
    timestamp = models.DateTimeField(null=True, blank=True)


class Wallet(models.Model):
    owner = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    uuid = models.UUIDField(default=uuid.uuid4, unique=True)
    balance = models.BigIntegerField(default=0)

    def deposit(self, amount: int):
        self.balance = F("balance") + amount
        self.save()

Task

@shared_task
def perform_tx_periodic():
    
    accpeted_tx = Transaction.objects.select_related("wallet__owner").select_for_update().filter(
            status="bank_acceptance", timestamp__lte=timezone.now()
        )
    with transaction.atomic():
        for tx in accpeted_tx:
            wallet = Wallet.objects.select_for_update().get(id=tx.wallet_id)
            if tx.amount < wallet.balance:
                logger = Logger(user=tx.wallet.owner)
                wallet.balance = F("balance") - tx.amount
                wallet.save(update_fields=["balance"])
                tx.status = tx.DONE_STATUS
                logger.withdraw_log(amount=tx.amount)
            else:
                tx.status = tx.FAILED_STATUS

            tx.save(update_fields=["status"])

    for query in connection.queries:
        print(query["sql"])

how can i lock wallet objects through foreign key on transaction model?

this code has query n+1 i want to decrease queries

after all, is there any way to optimize this task? for example, decrease volume of code, or perform fewer query

consider this: each wallet, may have multiple transaction at same time, so if i don't lock each wallet row, i may have non-positive balance in my wallet. we should handle the this situation to

0

There are 0 best solutions below