Django でこの競合状態に対処するにはどうすればいいですか? 質問する

Django でこの競合状態に対処するにはどうすればいいですか? 質問する

このコードは、オブジェクトを取得または作成し、必要に応じて更新することを目的としています。このコードは、Web サイトで実稼働中に使用されています。

場合によっては、データベースがビジー状態の場合、「DoesNotExist: MyObj に一致するクエリが存在しません」という例外がスローされます。

# 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データベースを使用しています。

この問題を安全に処理するにはどうすればよいでしょうか?

ベストアンサー1

これは、ここでと同じ問題の派生である可能性があります:

このループでは、更新されたオブジェクト数が 5 秒ごとに表示されないのはなぜですか?

基本的に取得または作成 失敗する可能性がある- ソースを見てみると、get、if-problem: save+some_trickery、if-still-problem: get again、if-still-problem: surrender and raise となっていることがわかります。

つまり、2 つのスレッド (またはプロセス) が同時に実行されていてcreate_or_update_myobj、両方が同じオブジェクトを取得または作成しようとしている場合は、次のようになります。

  • 最初のスレッドはそれを取得しようとしますが、まだ存在しません。
  • したがって、スレッドはそれを作成しようとしますが、オブジェクトが作成される前に...
  • ...2番目のスレッドがそれを取得しようとしますが、これは明らかに失敗します
  • 現在、MySQLdb データベース接続のデフォルトの AUTOCOMMIT=OFF と、REPEATABLE READ シリアル化可能レベルにより、両方のスレッドが MyObj テーブルのビューをフリーズしています。
  • その後、最初のスレッドはオブジェクトを作成し、それを正常に返しますが...
  • unique...2番目のスレッドは制約に違反するため何も作成できません
  • 面白いことに、getMyObjテーブルのビューが凍結されているため、2番目のスレッドの後続のスレッドでは、最初のスレッドで作成されたオブジェクトが表示されません。

したがって、何かを安全にしたい場合はget_or_create、次のようなことを試してください。

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

2010年5月27日に編集

この問題には、REPEATABLE READ ではなく READ COMMITED 分離レベルを使用するという 2 番目の解決策もあります。ただし、この方法は (少なくとも MySQL では) あまりテストされていないため、バグや問題が多く発生する可能性があります。ただし、少なくとも、途中でコミットすることなく、ビューをトランザクションに結び付けることができます。

2012年1月22日に編集

この質問に関連する、MySQL と Django に関する優れたブログ投稿 (私のものではありません) をいくつか紹介します。

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

おすすめ記事