我们假设我正在开发一个流量很大的在线商店。出售的物品需求量很大,但也非常有限。我需要确保它们不会被超卖。
目前我有这样的事情:
$order->addProduct($product);
$em->persist($order);
if($productManager->isAvailable($product)){
$em->flush();
}
但是,我认为如果两个订单在很短的时间内进入,我们仍然允许超卖产品。还有哪些其他可能性来确保产品绝对不会被超卖?
答案 0 :(得分:1)
您需要在交易中使用pessimistic lock。
假设您的Product
实体有count
字段,其中包含剩余的项目数。用户购买商品后,您将减少该字段。
在这种情况下,您需要一个悲观的写锁。基本上,它会阻止其他进程读取和/或更新一行尝试获取悲观锁。这些进程保持锁定状态,直到通过提交或回滚或超时后锁定行的事务结束。
因此,您启动一个事务,获取一个锁,检查是否有足够的项目,将它们添加到订单,减少项目数量并提交交易:
$em->beginTransaction();
try {
$em->lock($product, LockMode::PESSIMISTIC_WRITE);
if ($product->getCount() < $numberOfItemsBeingPurchased) {
throw new NotEnoughItemsLeftInStock;
}
$order->addItem($product, $numberOfItemsBeingPurchased);
$product->decreaseCount($numberOfItemsBeingPurchased);
$em->commit();
} catch (Exception $e) {
$em->rollback();
throw $e;
}
我建议在这里抛出一个例外,因为2个用户同时购买最后一个项目是一种特殊情况。当然,您应该使用某种项目计数检查 - 验证约束或其他 - 之前运行此代码。因此,如果用户已经超过了该检查但另一个用户在检查之后购买了最后一项,而之前当前用户实际购买了它,那么这是一种特殊情况。
另请注意,您应该在单 HTTP请求期间启动和结束事务。我的意思是,不在一个HTTP请求中锁定一行,等待用户完成购买并在此之后释放锁定。如果您希望用户能够将物品保留在购物车中一段时间(例如在现实世界的购物车中),请使用其他方法,例如通过减少库存中剩余物品的数量来为用户预留产品一段时间如果在超时之后通过添加该数量的项目来释放它。
答案 1 :(得分:-1)
关于Doctrine2
的完整章节谈论并发性,这正是你所需要的。
您需要创建一个transactionnal自定义查询,并在查询时锁定您的表。这一切都在这里解释:Transactions and Concurrency