Brief
In one of my views I make external API Call to charge user's credit card. This view is not invoked by user himself - it's requested by staff user. I want to avoid race condition that allows two staff users to charge payment at the same time (or in very short period).
Code
Let's say my (much simplified) view's payment method looks like this in pseudocode:
def make_payment(order):
"""
order - instance of Order model
"""
payment = Payment.objects.get_or_create(order=order)
if not payment.paid:
authorised = ExternalAPI.charge(payment)
if authorised:
payment.paid = True
payment.save()
Problem
My ATOMIC_REQUESTS setting is set to True (it has to stay that way). That means, that payment.paid set to True won't be commited until client gets his response. In case when a payment instance already exists, but was not paid yet there is race condition that would allow two clients to simultanously call view and thus charge payment twice. One of them would be then probably returned OperationalError (database is locked), but still most of the code would be executed twice. I actually tried it by simply making two requests at once from console and it happened. How can I avoid it?
What I've tried
Since I'm already using Redis in my project, I installed django-redis and tried to use cache.lock(). It helped a little, but since my views are atomic, and I have to acquire and release lock in my view's code, database transaction is commited after I release the lock. That still leaves some small window (after lock release and before transaction commit) for second request to kick in and charge payment again.
Any hints, SO?
Aucun commentaire:
Enregistrer un commentaire