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