2009-10-29 50 views
20

Djangoモデルの同時実行性はどのように処理しますか?同じレコードを読んでいる別のユーザーがレコードの変更を上書きすることは望ましくありません。Djangoモデルの同時実行制御

+0

可能な重複から取得され

class ConcurrentModel(models.Model): _change = models.IntegerField(default=0) class Meta: abstract = True def save(self, *args, **kwargs): cls = self.__class__ if self.pk: rows = cls.objects.filter( pk=self.pk, _change=self._change).update( _change=self._change + 1) if not rows: raise ConcurrentModificationError(cls.__name__, self.pk) self._change += 1 super(ConcurrentModel, self).save(*args, **kwargs) 

行の暗黙DBレベルのロックを解除:// stackoverflowの.com/questions/320096/django-how-can-i-protect-of-database-entries-of-database-entriesの変更 – Tony

答えて

22

短い答えですが、これは実際にはDjangoの質問ではありません。

同時実行制御は、技術的な質問として提示されることがよくありますが、多くの点で機能要件の問題です。アプリケーションをどのように動作させたい/必要としますか?私たちがそれを知るまで、Django固有のアドバイスを与えるのは難しいでしょう。

はしかし、私はとりとめのないように感じるので、ここで

は、同時実行制御の必要性に直面したとき、私は自分自身を依頼する傾向がある二つの質問があります...行く:

  • はどのように可能性が高いことがあるが2人のユーザーが同じレコードを同時に変更する必要がありますか?
  • レコードへの変更が失われた場合、ユーザーに与える影響は?

衝突の可能性が比較的高い場合、または変更を失うことの影響が深刻な場合は、何らかの形の悲観的ロックを検討している可能性があります。悲観的なスキームでは、各ユーザーは変更のためにレコードを開く前に論理ロックを取得する必要があります。

ペシミスティックロックは非常に複雑です。ロックへのアクセスを同期させ、フォールトトレランス、ロックの有効期限を確認し、スーパーユーザーがロックを上書きできるようにしたり、ユーザーがロックを持っているかどうかを確認したりできます。

Djangoでは、ロックされたレコードに別のロックモデルや何らかの種類の 'ロックユーザー'外部キーを実装することができます。ロックテーブルを使用すると、ロックが取得されたときの格納、ユーザー、メモなどの点で柔軟性が増します。あらゆる種類のレコードをロックするために使用できる汎用ロックテーブルが必要な場合は、 django.contrib.contenttypes frameworkしかし、これはすぐに抽象化宇宙飛行士の症候群に派生することができます。

衝突が起こりにくい場合や、紛失した変更が簡単に再作成された場合、機能的には楽観的な並行処理技術を使用できます。この手法は簡単で実装が簡単です。本質的には、バージョン番号または変更のタイムスタンプを追跡して、あなたが突っ込んだものとして検出した変更を拒否するだけです。

機能設計の観点からは、これらの同時の変更エラーがユーザーにどのように表示されるかを考慮する必要があります。ジャンゴの面では、楽観的同時実行制御は、モデルクラスに保存する方法をオーバーライドして実装することができ

...

def save(self, *args, **kwargs): 
    if self.version != self.read_current_version(): 
     raise ConcurrentModificationError('Ooops!!!!') 
    super(MyModel, self).save(*args, **kwargs) 

そして、もちろん、これらの同時実行メカニズムのいずれかが強固であるためには、あなたは考慮する必要がありますtransactional control。あなたのトランザクションのACIDプロパティを保証できない場合、これらのモデルは完全に実行可能ではありません。

+0

あなたが書いた最後の例は私が知りたいものでしたので、モデルの保存メソッド。私は別のフレームワークから、コントロールと比較するためにプロパティを "compareallvalues"に設定するので、私はDjangoでそれを実装する方法がわからず、開始チュートリアルで例を見つけなかったか、それを逃しました。私はDjangoがプログラマーによって他のフレームワークが行われているいくつかのタスクを自動化しているので、このタスクは私が最初に参照したフレームワークのように自動化できると思った – Pablo

+0

ええ、私の個人的意見は、フレームワークは、 /技術的な意味。 YMMV ... –

+2

以下に述べるように、最後のスニペットのコードは壊れています。バージョンチェックと保存メソッドの間に変更が残っているかもしれません。 – julkiewicz

10

「バージョン番号またはタイムスタンプを保持する」とは思えません。

self.version == self.read_current_version()Trueの場合、super().save()を呼び出す直前に他のセッションでバージョン番号が変更される可能性があります。

+2

問題のテーブルをロックしない限り、これは正しいです。しかし、あなたが参照している競合状態を避ける必要があるDjangoモデルでテーブルロックを簡略化するためのデコレータがあります。 – Cerin

2

私はJoe Hollowayの入門説明に同意します。彼の答えの非常に最後の部分に比べて

私は作業スニペットに貢献したい(「ジャンゴの面では、楽観的同時実行制御は、モデルクラスのメソッドを保存オーバーライドして実装することができます...」)

あなたがDBトランザクション内にある場合

は、あなた自身のモデル

のための祖先として、以下のクラスを使用することができます(例:外側のスコープでtransaction.atomic使用して)次のPython文が安全かつ一貫している

実際には、1回のシングルショットで、filter + update文は一種のtest_anレコードのd_set:バージョンを確認し、暗黙的にdbレベルのロックを取得します。 次の「保存」は、レコードのフィールドを更新して、そのモデルインスタンスで動作する唯一のセッションであることを確認できます。 最終コミット(transaction.atomicで__exit__によって自動的に実行されるなど)これはHTTPの

https://bitbucket.org/depaolim/optlock/src/ced097dc35d3b190eb2ae19853c2348740bc7632/optimistic_lock/models.py?at=default
関連する問題