2016-05-15 6 views
4

オブジェクトをデータベースに保存するDjangoアプリケーションと、それらのオブジェクトのいくつかで定期的に処理を行うセロリのタスクがあります。問題は、のオブジェクトを処理するためにセロリタスクによって選択されたの後に削除できますが、の前には、のセロリタスクが実際に処理および保存を完了しています。したがって、セールスタスクが.save()を呼び出すと、オブジェクトが削除されたにもかかわらずオブジェクトがデータベースに再表示されます。もちろん、ユーザーのために本当にうんざりです。オブジェクトやベールがDjangoで削除された場合の更新方法

そこでここでは問題を示すいくつかのコードです:

def my_delete_view(request, pk): 
    thing = Thing.objects.get(pk=pk) 
    thing.delete() 
    return HttpResponseRedirect('yay') 

@app.task 
def my_periodic_task(): 
    things = get_things_for_processing() 
    # if the delete happens anywhere between here and the .save(), we're hosed 
    for thing in things: 
     process_thing(thing) # could take a LONG time 
     thing.save() 

私はアトミック・ブロックとオブジェクトが実際にそれを保存する前に存在しているかどうかをテストするには、トランザクションを追加することによって、それを修正しようと考えました:

@app.task 
def my_periodic_task(): 
    things = Thing.objects.filter(...some criteria...) 
    for thing in things: 
     process_thing(thing) # could take a LONG time 
     try: 
      with transaction.atomic(): 
       # just see if it still exists: 
       unused = Thing.objects.select_for_update().get(pk=thing.pk) 
       # no exception means it exists. go ahead and save the 
       # processed version that has all of our updates. 
       thing.save() 
     except Thing.DoesNotExist: 
      logger.warning("Processed thing vanished") 

これはこの種のことを行う正しいパターンですか?私はそれが生産でそれを実行して数日以内に動作するかどうかを調べるでしょうが、この種のものを達成するための他のよく受け入れられているパターンがあるかどうかを知ることはうれしいでしょう。

私が本当に望むのは、データベースにまだが存在する場合は、オブジェクトを更新できることです。 process_thingのユーザー編集と編集の間の競争は大丈夫ですが、process_thingの直前にrefresh_from_dbを入れてユーザーの編集内容を失う時間を最小限に抑えることができます。しかし、私は間違いなく、ユーザーがそれらを削除した後にオブジェクトを再表示させることはできません。あなたがセロリのタスクの処理時間のためにトランザクションを開いた場合

+1

私によく似ています – dkarchmer

答えて

-1

、それは「保存し、アトミック再び選択」のパターンのように思える十分です:

@app.task 
def my_periodic_task(): 
    things = Thing.objects.filter(...some criteria...) 
    for thing in things: 
     process_thing(thing) # could take a LONG time 
     try: 
      with transaction.atomic(): 
       # just see if it still exists: 
       unused = Thing.objects.select_for_update().get(pk=thing.pk) 
       # no exception means it exists. go ahead and save the 
       # processed version that has all of our updates. 
       thing.save() 
     except Thing.DoesNotExist: 
      logger.warning("Processed thing vanished") 

(これは私の元の質問と同じコードです)。

+0

downvoter精巧に注意してください? – mgalgs

0

、あなたはこのような問題を回避する必要があります。

@app.task 
@transaction.atomic 
def my_periodic_task(): 
    things = get_things_for_processing() 
    # if the delete happens anywhere between here and the .save(), we're hosed 
    for thing in things: 
     process_thing(thing) # could take a LONG time 
     thing.save() 

を時々、あなたはデータ上で作業していることを、フロントエンドに報告したいと思いますので、select_for_update()をクエリセットに追加することができます(おそらくget_things_for_processingにあります)。削除の責任を負うコードでは、dbが特定のレコードがロックされたと報告するときにエラーを処理する必要があります。今の

+0

ええ、私はむしろ可能な限り短い時間(私の質問に示されているように)ロックしておきたいと思います。私が提案したアプローチに全期間ロックしておくことに利点がありますか? – mgalgs

+0

はい - このレコードに格納されているデータを使用していて、処理を開始してから終了するまで何もしていないことを確かめなければなりません。 – Jerzyk

+0

しかしこれは私が必要とするものではありません:)。この質問で述べたように、私はユーザーの編集と 'process_thing'の編集との間のレースで大丈夫です。私はちょうど削除されたオブジェクトが再出現しないようにしたい。それが私が守ろうとしている唯一のことです。 – mgalgs

関連する問題