假设我有一个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 和 2 几乎同时调用
get_or_create_with_func
。 - 他们都尝试通过
obj.objects.create
- 假设线程 1 成功创建了它。然后它退出 try/except 块,然后执行
func_to_call
. - 线程 2 也尝试创建对象。
- 理想情况下,线程 2 将遇到
IntegrityError
. - 线程 1 完成调用
func_to_call
,它不会引发异常。 - 线程 2 “提交”(并且线程 1 也已经完成提交),因此它可以调用
get
,并按预期获取线程 1 写入的 DB 对象。
或者
- 线程 1 调用
func_to_call
并引发异常。 - 线程 1 导致 django 回滚事务,因此 DB 条目不存在。
- 线程 2 应该仍会遇到
IntegrityError
,但现在调用get
不会返回任何对象,因为线程 1 回滚了事务。
我的问题是,在第 4 步,create
由于表上的唯一约束而失败,还是会因为线程 1 尚未实际提交事务(假设func_to_call
相当慢)而通过?