首页 > 解决方案 > 当 Django 将可序列化事务隔离级别与 postgresql 一起使用时,哪些特定异常表示序列化失败?

问题描述

有时需要为 Django 中的数据库操作使用比默认的“已提交读”更高的隔离级别。 文档警告说:

在更高的隔离级别下,您的应用程序应该准备好处理因序列化失败而引发的异常。

但是哪些特定异常表明序列化失败,而不是查询或事务的其他问题?

序列化失败后的简单重试机制可能如下所示:

for retries in range(0, 3):
    try:
        with transaction.atomic():
            MyModel.objects.update(foo='bar')
    except StuffHappened:
        continue
    else:
        break

应该替换哪些特定异常,StuffHappened以便只有序列化失败而不是其他异常会导致重试?

Django 有多种Database ExceptionsTransaction Exceptions。其中一个/一些可能代表序列化失败吗?

我对此特别感兴趣。

标签: djangopostgresqlexception-handlingtransactionsisolation-level

解决方案


嗯,好问题。该文档暗示适当的例外是TransactionManagementError

TransactionManagementError针对与数据库事务相关的任何和所有问题提出。

但是,源代码提供了一个强有力的线索,表明它不是:

class TransactionManagementError(ProgrammingError):
    """Transaction management is used improperly."""
    pass

请注意,这是一个ProgrammingError,它确实用于指示程序员错误(即“使用不当”)。

如果我们查看 psycopg(用于 PostgreSQL 支持的 Python 适配器)的文档,我们会看到它会引发psycopg2.extensions.TransactionRollbackError

异常psycopg2.extensions.TransactionRollbackError(子类OperationalError

导致事务回滚的错误(死锁、序列化失败等)。

但是 Django 对此做了什么?好吧,如此所述,它将标准 Python DB API 2.0 异常包装在 Django 等效项中,并将__cause__属性设置为原始异常。因此,以下可能是您可以进行的最具体的检查:

from django.db import OperationalError
from psycopg2.extensions import TransactionRollbackError

for retries in range(0, 3):
    try:
        with transaction.atomic():
            MyModel.objects.update(foo='bar')
    except OperationalError as e:
        if e.__cause__.__class__ == TransactionRollbackError:
            continue
        else:
            raise            
    else:
        break

根据 PostgreSQL 公开的错误详细信息(可通过 获得e.__cause__.diag),可能会编写更具体的测试。

不过,一般来说,Python DB API 2.0 文档指出这OperationalError确实是事务问题的正确异常类型,因此希望捕获它是一个合理有效的与数据库无关的解决方案。


推荐阅读