0

フロントエンド部分とバックエンド部分の両方を持つ非常に複雑な Django アプリケーションを想像してみてください。一部のユーザーは、フロントエンド部分の一部のデータを変更します。一部のスクリプトは、バックエンド部分で同じデータを定期的に変更します。

例:

instance = SomeModel.objects.get(...)
# (long-running part where various fields are changed, takes from 3 to 20 seconds)
instance.field = 123
instance.another_field = 'abc'
instance.save()

その部分がいくつかのフィールドを変更している間に誰か (または何か) がインスタンスを変更すると、インスタンスが最近保存され、Python (Django) クラスからデータがダンプされるため、変更は失われます。言い換えれば、コード内の何かがデータを取得し、しばらく待ってからデータを保存する場合、最新の「セーバー」のみがそのデータを保存し、他のすべての (以前の) ものは変更を失います。

これは「負荷の高い」アプリであり、データベースの負荷 (Postgres を使用) は非常に高く、DB アクティビティや使用メモリの大幅な増加を引き起こすようなことは避けたいと考えています。

別の問題 - 多くのシグナルが添付されており、さらには save() メソッドがオーバーライドされているため、シグナルを壊したり、カスタムの save() または update() メソッドと互換性がない可能性があるものは避けたいと思います。

この状況であなたは何をお勧めしますか?そのための特別なアプリはありますか?取引?他に何か?

ありがとうございました!

4

1 に答える 1

2

これを防ぐ正しい方法は、 を使用select_for_updateして、読み取りと書き込みの間でデータが変更されないようにすることです。ただし、これにより行が更新のためにロックされるため、アプリケーションが大幅に遅くなる可能性があります。

1 つの解決策は、データを読み取り、長時間実行されるタスクを実行することです。次に、保存する前にトランザクションを開始し、データを再度読み取りますがselect_for_update、元のデータが変更されていないことを確認します。データがまだ同じ場合は、保存します。データが変更された場合は、実行時間の長いタスクを中止して再実行します。そうすれば、ロックをできるだけ短く保持できます。

何かのようなもの:

success = False
while not success:
  instance1 = SomeModel.objects.get(...)
  # (long-running part)

  with django.db.transaction.atomic():
    instance2 = SomeModel.objects.select_for_update().get(...)
    # (compare relevant data from instance1 vs instance2)
    if unchanged:
      # (make the changes on instance2)
      instance2.field = 123
      instance2.another_field = 'abc'
      instance2.save()
      success = True

これが実行可能なアプローチであるかどうかは、長時間実行されるタスクが正確に何であるかによって異なります。また、ここに保存したデータをユーザーが上書きする可能性もあります。

于 2014-09-08T10:49:49.423 に答える