在django怎么处理这种比赛状况? [英] How do I deal with this race condition in django?

查看:123
本文介绍了在django怎么处理这种比赛状况?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这段代码应该是获取或者创建一个对象,如果需要的话更新它。代码在网站上生产使用。

This code is supposed to get or create an object and update it if necessary. The code is in production use on a website.

在某些情况下 - 数据库正忙时,会抛出异常DoesNotExist:MyObj匹配查询不存在。

In some cases - when the database is busy - it will throw the exception "DoesNotExist: MyObj matching query does not exist".

# Model:
class MyObj(models.Model):
    thing = models.ForeignKey(Thing)
    owner = models.ForeignKey(User)
    state = models.BooleanField()
    class Meta:
        unique_together = (('thing', 'owner'),)

# Update or create myobj
@transaction.commit_on_success
def create_or_update_myobj(owner, thing, state)
    try:
        myobj, created = MyObj.objects.get_or_create(owner=user,thing=thing)

    except IntegrityError:
        myobj = MyObj.objects.get(owner=user,thing=thing)
        # Will sometimes throw "DoesNotExist: MyObj matching query does not exist"

    myobj.state = state
    myobj.save()

我在ubuntu上使用innodb mysql数据库。

I use an innodb mysql database on ubuntu.

我如何安全地处理wi这个问题?

How do I safely deal with this problem?

推荐答案

这可能是一个与此相同的问题的脱机:

This could be an off-shoot of the same problem as here:

为什么这个循环会每五秒显示一个更新的对象数吗?

基本上 get_or_create 可以失败 - 如果你看看它的来源,会看到它是:get,if-problem:save + some_trickery,if-still-problem:再一次,if-still-problem:投降和提高。

Basically get_or_create can fail - if you take a look at its source, there you'll see that it's: get, if-problem: save+some_trickery, if-still-problem: get again, if-still-problem: surrender and raise.

这意味着如果有两个并发线程(或进程)运行 create_or_update_myobj ,那么尝试get_or_create相同对象,然后:

This means that if there are two simultaneous threads (or processes) running create_or_update_myobj, both trying to get_or_create the same object, then:


  • 第一个线程尝试获取它 - 但它还不存在,

  • 所以,线程尝试创建它,但是在创建对象之前...

  • ...第二个线程尝试获取它 - 这显然失败了

  • 现在,由于MySQLdb数据库连接的默认AUTOCOMMIT = OFF,REPEATABLE READ可序列化级别,两个线程都冻结了MyObj表的视图。

  • 线程创建它的对象并优雅地返回它,但是...

  • ...第二个线程不能创建任何东西,因为它会违反 unique 约束

  • 有趣的是,第二个线程的后续 get 不会看到第一个线程中创建的对象,由于冻结查看MyObj表

  • first thread tries to get it - but it doesn't yet exist,
  • so, the thread tries to create it, but before the object is created...
  • ...second thread tries to get it - and this obviously fails
  • now, because of the default AUTOCOMMIT=OFF for MySQLdb database connection, and REPEATABLE READ serializable level, both threads have frozen their views of MyObj table.
  • subsequently, first thread creates its object and returns it gracefully, but...
  • ...second thread cannot create anything as it would violate unique constraint
  • what's funny, subsequent get on the second thread doesn't see the object created in the first thread, due to the frozen view of MyObj table

所以,如果你想安全地 get_or_create 任何东西,尝试这样:

So, if you want to safely get_or_create anything, try something like this:

 @transaction.commit_on_success
 def my_get_or_create(...):
     try:
         obj = MyObj.objects.create(...)
     except IntegrityError:
         transaction.commit()
         obj = MyObj.objects.get(...)
     return obj



于27/05/2010编辑



还有第二个解决方案 - 使用READ COMMITED隔离级别,而不是REPEATABLE READ。但是它的测试较少(至少在MySQL中),所以可能会有更多的错误/问题 - 但是至少它可以将交易的交易视图绑定在中间。

Edited on 27/05/2010

There is also a second solution to the problem - using READ COMMITED isolation level, instead of REPEATABLE READ. But it's less tested (at least in MySQL), so there might be more bugs/problems with it - but at least it allows tying views to transactions, without committing in the middle.

这是关于MySQL和Django的一些很好的博客文章(不是我的),与这个问题有关:

Here are some good blog posts (not mine) about MySQL and Django, related to this question:

http: //www.no-ack.org/2010/07/mysql-transactions-and-django.html

http://www.no-ack.org/2011/05/broken- transaction-management-in-mysql.html

这篇关于在django怎么处理这种比赛状况?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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