Я реализую игрушечное приложение для Eauction в Django, и я не совсем понимаю, как лучше всего обрабатывать параллелизм в приведенном ниже коде. Я не уверен, какой из моих кандидатов на решение (или любой другой) лучше всего подходит для дизайна Django. Я довольно новичок в Django / python, и мои ноу-хау в SQL ржавые, поэтому извиняюсь, если это не сложно.
Требование: пользователи могут делать ставки на продукты. Тендерные предложения принимаются только в том случае, если они превышают предыдущие заявки на тот же продукт.
Вот урезанная версия моделей:
class Product(models.Model):
name = models.CharField(max_length=20)
class Bid(models.Model):
amount = models.DecimalField(max_digits=5, decimal_places=2)
product = models.ForeignKey(Product)
и просмотр ставок. Здесь происходят условия гонки (см. Комментарии):
def bid(request, product_id):
p = get_object_or_404(Product, pk=product_id)
form = BidForm(request.POST)
if form.is_valid():
amount = form.cleaned_data['amount']
# the following code is subject to race conditions
highest_bid_amount = Bid.objects.filter(product=product_id).aggregate(Max('amount')).get('amount__max')
# race condition: a bid might have been inserted just now by another thread so highest_bid_amount is already out of date
if (amount > highest_bid_amount):
bid = Bid(amount=amount, product_id=product_id)
# race condition: another user might have just bid on the same product with a higher amount so the save() below is incorrect
b.save()
return HttpResponseRedirect(reverse('views.successul_bid)'
Кандидаты на решение, которые я рассматривал до сих пор:
- Я прочитал документ Django о транзакциях, но я не знаю, как применить их к моей проблеме. Поскольку база данных не знает о требовании повышения ставок, она не может заставить Django выдать IntegrityError. Есть ли способ определить это ограничение во время определения модели? Или он неправильно понял API транзакции?
- Хранимая процедура может позаботиться о логике назначения ставок. Пока это кажется мне «лучшим» выбором, но он переносит обработку состояния гонки на базовую систему баз данных. Однако, если это хороший подход, это решение может быть объединено с решением 1?
- Я подумал об использовании вызова select_for_update для блокировки ставок для этого продукта. Однако это, похоже, не является решением, поскольку, насколько я понимаю, это не повлияет на создание новых ставок?
Список пожеланий:
- Если есть какая-либо возможность, я бы хотел не блокировать всю таблицу ставок, поскольку ставки на другие продукты не могут быть затронуты в любом случае.
- Если есть хорошее решение на уровне приложения, я хотел бы сохранить код независимым от базовой системы баз данных.
Большое спасибо за ваши мысли!