我在一个Electron 商务网站与购物车和产品.产品的主键被添加到字典'cart_content'
中的用户会话数据.该字典将产品的主键作为键,将给定产品的数量作为值.
我成功地复制了一个竞争条件错误,我同时添加了两个产品,数量足够大,所以两个添加中只有一个卖完了产品(例如,剩下3个产品,再添加3个).这两个请求将购物车增加两倍,但没有足够的库存(在我们的示例中,剩余3个,但购物车中有6个).
如何防止竞争条件发生,如上面给出的例子或在多用户情况下?例如,是否有某种锁定系统可以防止add_to_cart()
被异步执行?
Edit: I switched from SQLite to PostgreSQL because I wanted to test 100.
Core/Models.py:
class Product(models.Model):
…
number_in_stock = models.IntegerField(default=0)
@property
def number_on_hold(self):
result = 0
for s in Session.objects.all():
amount = s.get_decoded().get('cart_content', {}).get(str(self.pk))
if amount is not None:
result += int(amount)
return result
…
购物车/views.py:
def add_to_cart(request):
if (request.method == "POST"):
pk = request.POST.get('pk', None)
amount = int(request.POST.get('amount', 0))
if pk and amount:
p = Product.objects.get(pk=pk)
if amount > p.number_in_stock - p.number_on_hold:
return HttpResponse('1')
if not request.session.get('cart_content', None):
request.session['cart_content'] = {}
if request.session['cart_content'].get(pk, None):
request.session['cart_content'][pk] += amount
else:
request.session['cart_content'][pk] = amount
request.session.modified = True
return HttpResponse('0')
return HttpResponse('', status=404)
购物车/urls.py:
urlpatterns = [
…
path("add-to-cart", views.add_to_cart, name="cart-add-to-cart"),
…
]
Cart/templates/cart/html/atoms/add_to_cart.html:
<div class="form-element add-to-cart-amount">
<label for="addToCartAmount{{ product_pk }}"> {% translate "Amount" %} : </label>
<input type="number" id="addToCartAmount{{ product_pk }}" />
</div>
<div class="form-element add-to-cart">
<button class="btn btn-primary button-add-to-cart" data-product-pk="{{ product_pk }}" data-href="{% url 'cart-add-to-cart' %}"><span> {% translate "Add to cart" %} </span></button>
</div>
Cart/Static/Cart/js/main.js:
$(".button-add-to-cart").click(function(event) {
event.preventDefault();
let product_pk = $(this).data("productPk");
let amount = $(this).parent().parent().find("#addToCartAmount" + product_pk).val();
$.ajax({
url: $(this).data("href"),
method: 'POST',
data: {
pk: product_pk,
amount: amount
},
success: function(result) {
switch (result) {
case '1':
alert(gettext('Amount exceeded'));
break;
case '0':
alert(interpolate(gettext('Successfully added %s items to the cart.'), [amount]))
break;
default:
alert(gettext('Unknown error'));
}
}
});
});
用于重现争用条件的Java脚本.当然,它并不总是在第一次就奏效.只要重复两到三次,直到你得到我提到的行为:
async function test() {
document.getElementsByClassName('button-add-to-cart')[0].click();
}
test(); test();