我有一个基于Symfony的移动应用程序和服务器,它为移动应用程序提供API。
我有一种情况,用户可以在这种情况下Post
。当用户喜欢Post
时,我在ManyToMany表中添加了一个条目,表明此特定用户喜欢此特定Post
(步骤1)。然后在Post
表中增加likesCounter(步骤2)。然后在User
表格中,我增加了用户的游戏化点数(因为他喜欢Post
)(步骤3)。
因此,存在许多用户同时喜欢特定Post
并且发生死锁(在Post
表或User
表上)的情况。
怎么办呢?在Doctrine Docs我可以看到这样的解决方案:
<?php
try {
// process stuff
} catch (\Doctrine\DBAL\Exception\RetryableException $e) {
// retry the processing
}
但我应该在catch
部分做什么?重试整个喜欢的过程(步骤1到3),例如3次,如果失败,则将BadRequest返回给移动应用程序?或其他什么?
我不知道这是不是一个很好的例子,因为我可能会尝试重建这个过程,所以不会发生僵局,但我想知道如果真的发生了我该怎么办?
答案 0 :(得分:0)
我要做的是将所有喜欢发布到队列中并使用prepros使用它们,以便您可以在一个帖子上对更新进行分组。
如果你坚持让你保持目前的实施,你可以按照你自己的建议走这条路:
<?php
for ($i = 0; $i < $retryCount; $i++) {
try {
// try updating
break;
} catch (\Doctrine\DBAL\Exception\RetryableException $e) {
// you could also add a delay here
continue;
}
}
if ($i === $retryCount) {
// throw BadRequest
}
&#13;
这是一个丑陋的解决方案,我不建议。僵局不应该被避免&#34;通过重试或使用延迟。另请查看batch consumer并使用相同的重试系统,但不要等待发生死锁。
答案 1 :(得分:0)
问题是Symfony Entity Manager失败后 - 它会关闭数据库连接,即使你捕获了ORMException,也无法继续使用db。
第一个好的解决方案是使用rabbitmq或其他队列实现处理你的'喜欢'异步。
步骤一步:
{type: 'like', user:123, post: 456}
您可以让几个尝试根据postId获取锁定的消费者。如果两个消费者试图更新相同的帖子 - 其中一个将无法获得锁定。但没关系,你可以在之后消费失败的消息。
第二种解决方案是拥有特殊的表格,例如post_likes(userId,postId,timestamp)。您的端点可以同步在此表中创建新行。你可以用这张桌子计算一些帖子上的'喜欢'。或者您可以编写一些cron脚本,它将更新此表中的帖子之类的数量。
答案 2 :(得分:0)
我不同意Stefan的说法,正如MySQL文档所说,死锁是正常的:
通常,您必须编写应用程序,以便在由于死锁而回滚时,它们始终准备重新发出事务。
然而,Stefan提出的循环是正确的解决方案。除了它没有重要意义:在Doctrine抛出异常之后,EntityManager变得无法使用,您必须使用ManagerRegistry实例中的resetManager()在catch子句中创建一个新的。
当我和你有完全相同的关注时,我在网上搜索但是找不到任何完全满意的答案。所以我弄脏了手,然后回来了一篇文章,你可以找到我上面所说的实现例子: