如何避免在Django中使用独特检查的竞争条件 [英] How to avoid race condition with unique checks in Django

查看:146
本文介绍了如何避免在Django中使用独特检查的竞争条件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的模型:

  class InvitationRequest(models.Model):
email = models.EmailField(max_length = 255,unique = True)



  class InvitationRequestForm(forms.ModelForm):
class Meta:
model = InvitationRequest

现在,假设我以标准方式处理它:

  form = InvitationRequestForm(request.POST)
如果form.is_valid():
form.save()

有一个竞争条件,因为验证执行一个简单的 SELECT 查询以确定是否已经存储了此类电子邮件,如果一切正常,则继续执行 form.save()行。如果并发进程在完全相同的时刻执行相同的操作,那么两个表单都将被验证,两个进程将调用 form.save()其他将导致 IntegrityError



处理此问题的标准方法是什么? >



我想在表单对象中有一个标准错误,所以我可以将其传递给模板,并通知用户有关问题。



我知道:




  • 我可以用try / except包装所有内容,并手动添加新的错误

  • 我可以用 SERIALIZABLE 事务(在MySQL中执行下一个键锁定每个选择)包装所有内容

  • 我可以使用覆盖 Model._perform_unique_checks 并使
    使用 select_for_update (works因为下一个键锁定)

  • 我可以获取表级别的独占锁定



这些解决方案中没有一个是吸引人的,我也在使用PostgreSQL,这个区域与MySQL不同。

解决方案

方式是不要处理这个,因为:


  1. 您的案例失败的概率接近0;

  2. 失败的严重性非常低。

如果由于某种原因,您必须确保问题不会发生,你是自己的。



我没有详细分析事件的顺序,但我认为使用SERIALIZABLE隔离级别不会真的有帮助,它只会导致 IntegrityError (或 DatabaseError )在不同的地方被提出。



覆盖 Model._perform_unique_checks 听起来像一个坏主意,你最好远离猴子修补,如果可能的话(这里它可能)。 p>

至于使用表锁防止可能出现的错误...嗯,我不是一个大粉丝,所以我不能推荐这个。



以下是类似问题的一个很好的答案: https://stackoverflow.com/a/3523439/176186 - 我同意抓住 IntegrityError ,然后重试可能是解决问题的最简单的方法。



编辑:我发现这个: Symfony2 - 如何从表单提交后的唯一约束错误中恢复?,我同意@ pid的回答。


I have a simple model:

class InvitationRequest(models.Model):
    email = models.EmailField(max_length=255, unique=True)

And a simple model form:

class InvitationRequestForm(forms.ModelForm):
    class Meta:
        model = InvitationRequest

Now, assuming that I attempt to process it in a standard way:

form = InvitationRequestForm(request.POST)
if form.is_valid():
    form.save()

There is a race condition because validation performs a simple SELECT query to determine whether or not such email is already stored, and if everything is fine then it proceeds to form.save() line. If there is a concurrent process that does the same at exactly the same moment, then both forms will validate and both processes will call form.save() thus one will succeed and the other will fail causing an IntegrityError.

What is the standard way to handle this?

I want to have a standard error in the form object so I can pass it on to the template and notify user about the problem.

I know that:

  • I can wrap everything with try/except and add new error to my form manually
  • I can wrap everything with SERIALIZABLE transaction (in MySQL as it performs next key locking fo every select then)
  • I can use override Model._perform_unique_checks and make it use select_for_update (works with MySQL because of next key locking)
  • I can acquire table-level exclusive lock

None of these solutions is appealing, also I am using PostgreSQL which differs from MySQL in this area.

解决方案

The standard way is to NOT handle this, as:

  1. the probability of the failure in your case is close to 0;
  2. the severity of the failure is very low.

If, for some reason, you have to be sure that the problem won't happen, you are on your own.

I haven't analyzed the sequence of events in detail but I think that using the SERIALIZABLE isolation level won't really help, it will only cause IntegrityError (or DatabaseError) to be raised in a different place.

Overriding Model._perform_unique_checks sounds to me like a bad idea, you better stay away from monkey patching if possible (and here it is possible).

As for using the table lock to prevent unlikely errors... Well, I'm not a big fan so I cannot recommend that either.

Here's a nice answer to a similar question: https://stackoverflow.com/a/3523439/176186 - I concur that catching IntegrityError and retrying is probably the easiest sane way to deal with the problem.

EDIT: I found this: Symfony2 - how to recover from unique constraint error after form submission? and I agree with @pid's answer.

这篇关于如何避免在Django中使用独特检查的竞争条件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆