1

假设我有一个get_or_create符合这个答案的替换方法,除了我需要将此数据库事务与外部 API 调用结合起来(检查是否存在 S3 存储桶)。假设在数据库级别(MySQL InnoDB)存在唯一约束。我正在使用 eventlet 在多个 Gunicorn 工作进程中运行此代码。

@transaction.commit_on_success
def get_or_create_with_func(obj, func_to_call=None, **kwargs):
    try:
        result, created = obj.objects.create(**kwargs), True
    except IntegrityError:
        transaction.commit()
        result, created = obj.objects.get(**kwargs), False

    func_result = None
    if func_to_call is not None and created:
        func_result = func_to_call()

    return result, created, func_result

事务的成功取决于func_to_call成功返回(不引发异常)。如果它引发异常,显然事务将回滚。

我不确定的是,如果 2 个线程同时使用这个函数,create仍然会将一个条目保存到数据库中(这样IntegrityError就会引发并且get调用返回一个对象),或者它是否必须“等待”直到func_to_call完成?

基本上我在想象这种情况,上面的函数是这样调用的:

query_result, created, func_result = get_or_create_with_func(MyModel, external_api_func, model_column=some_value)
  1. 线程 1 和 2 几乎同时调用get_or_create_with_func
  2. 他们都尝试通过obj.objects.create
  3. 假设线程 1 成功创建了它。然后它退出 try/except 块,然后执行func_to_call.
  4. 线程 2 也尝试创建对象。
  5. 理想情况下,线程 2 将遇到IntegrityError.
  6. 线程 1 完成调用func_to_call,它不会引发异常。
  7. 线程 2 “提交”(并且线程 1 也已经完成提交),因此它可以调用get,并按预期获取线程 1 写入的 DB 对象。

或者

  1. 线程 1 调用func_to_call 并引发异常
  2. 线程 1 导致 django 回滚事务,因此 DB 条目不存在。
  3. 线程 2 应该仍会遇到IntegrityError,但现在调用get不会返回任何对象,因为线程 1 回滚了事务。

我的问题是,在第 4 步,create由于表上的唯一约束而失败,还是会因为线程 1 尚未实际提交事务(假设func_to_call相当慢)而通过?

4

0 回答 0