Django和MySQL设置中死锁后自动重试的钩子可用

6
我在Django中使用mysql数据库的innoDB表。在调查错误时,遇到了如下错误:

OperationalError: (1213, 'Deadlock found when trying to get lock; try restarting transaction')

我看到了Omry这个答案。在答案的最后一部分,他建议客户端应该自动重试。
我正在尝试将此逻辑放入代码中,但同时是否有任何可用于django的钩子?这样我们就可以在死锁的情况下设置3次自动重试。此外,如果有人能够给出在代码中放置此逻辑的示例(我正在使用django过滤器),那就更好了。
PS:我本可以在Omry的答案下提问,但我的积分不足50分,并且我也想请教django专家。

你正在执行哪些代码,它们在你的Django项目中的什么位置? - Timmy O'Mahony
嗨Timmy,感谢您的关注。我使用django_auth_ldap将我的LDAP服务器与数据库同步。在manage_user调用之一(创建/删除/更新用户详细信息),我会使用ldap_backend.populate_user(<email>)[这是为了将所有LDAP组与django auth_user_groups同步]。这会导致MySQL进入死锁状态。 - rajalokan
1个回答

10

这是一个旧问题,但由于没有人发布答案,所以在这里。

为了在死锁发生时重试查询,我所做的是猴子补丁django的CursorWrapper类的"execute"方法。每当进行查询时都会调用此方法,因此它将适用于整个ORM,并且您不必担心项目中的死锁:

import django.db.backends.utils
from django.db import OperationalError
import time

original = django.db.backends.utils.CursorWrapper.execute

def execute_wrapper(*args, **kwargs):
    attempts = 0
    while attempts < 3:
        try:
            return original(*args, **kwargs)
        except OperationalError as e:
            code = e.args[0]
            if attempts == 2 or code != 1213:
                raise e
            attempts += 1
            time.sleep(0.2)

django.db.backends.utils.CursorWrapper.execute = execute_wrapper
代码的作用是:它会尝试运行查询,如果出现 OperationalError 错误并且错误代码为 1213(死锁),它将等待 200 毫秒并重试。它将执行此操作 3 次,如果在 3 次后问题没有得到解决,则会引发原始异常。应该在 Django 项目加载到内存时执行此代码,因此将其放置在任何应用程序的 __init__.py 文件中是一个不错的选择(我将其放在了项目主目录的 __init__.py 文件中 - 与您的 Django 项目同名的那个目录)。希望这能帮助未来的读者。

非常感谢,我正在使用类似的方法来解决“OperationalError: (2006,'MySQL server has gone away')”问题,但是我没有编写自己的包装代码,而是使用了retry - Marcos Dione
是的,这个解决方案运行良好。现在我们遇到了以下错误。OperationalError: (1305, 'SAVEPOINT s139806324311104_x9 不存在')。我的views.py中的事务是原子性的。有人可以帮忙解决这个问题吗? - Murthy Pydikondala

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接