如何 avoid/fix django 的 DatabaseTransactionError

How to avoid/fix django's DatabaseTransactionError

我有以下(释义)代码受竞争条件的影响:

def calculate_and_cache(template, template_response):
    # run a fairly slow and intensive calculation:
    calculated_object = calculate_slowly(template, template_response)
    cached_calculation = Calculation(calculated=calculated_object,
                                     template=template,
                                     template_response=template_response)
    # try to save the calculation just computed:
    try:
        cached_calculation.save()
        return cached_calculation
    # if another thread beat you to saving this, catch the exception
    # but return the object that was just calculated
    except DatabaseError as error:
        log(error)
        return cached_calculation

它引发了 DatabaseTransactionError:

TransactionManagementError: An error occurred in the current transaction.
You can't execute queries until the end of the 'atomic' block.

The docs 对 DTE 的评价:

当退出一个原子块时,Django 会查看它是正常退出还是异常退出来决定是提交还是回滚....如果你试图运行 数据库查询在回滚发生之前,Django 将引发 TransactionManagementError。

但是 they 也有关于它们的更模糊的说法:

TransactionManagementError 是针对与数据库事务相关的所有问题而引发的。

我的问题,按普遍性升序排列:

  1. 捕获 DatabaseError 是否会通过让 save() 在返回对象的同时优雅地退出来真正解决竞争条件?
  2. 上述代码中的原子块从哪里开始到哪里结束?
  3. 我做错了什么,我该如何解决?

controlling transactions explicitly 上的 Django 文档有一个在原子块中捕获异常的示例。

在你的情况下,你似乎根本没有使用 atomic 装饰器,所以首先你需要添加所需的导入。

from django.db import transaction

然后您需要将可能引发数据库错误的代码移动到原子块中:

try:
    with transaction.atomic():
        cached_calculation.save()
    return cached_calculation
# if another thread beat you to saving this, catch the exception
# but return the object that was just calculated
except DatabaseError as error:
    log(error)
    return cached_calculation