11

私は次のジャンゴメソッドを持っています:

def setCurrentSong(request, player):    
  try:
     newCurrentSong = ActivePlaylistEntry.objects.get(
       song__player_lib_song_id=request.POST['lib_id'],
       song__player=player,   
       state=u'QE')
   except ObjectDoesNotExist:
     toReturn = HttpResponseNotFound()
     toReturn[MISSING_RESOURCE_HEADER] = 'song'    
     return toReturn
 
   try:
     currentSong = ActivePlaylistEntry.objects.get(song__player=player, state=u'PL')
     currentSong.state=u'FN'  
     currentSong.save()
   except ObjectDoesNotExist:  
     pass
   except MultipleObjectsReturned:     
     #This is bad. It means that
     #this function isn't getting executed atomically like we hoped it would be
     #I think we may actually need a mutex to protect this critial section :(
     ActivePlaylistEntry.objects.filter(song__player=player, state=u'PL').update(state=u'FN')

   newCurrentSong.state = u'PL'
   newCurrentSong.save()
   PlaylistEntryTimePlayed(playlist_entry=newCurrentSong).save()
   return HttpResponse("Song changed")

基本的に、特定の に対して、常に(再生中の) 状態を持つのはplayer1 つだけになるようにしたいと考えています。しかし、このメソッドを2回続けて素早く呼び出した結果、同じプレーヤーの曲を2つ取得し、状態が. プレイヤーが常に 1 つの再生中の曲しか持っていないという事実に依存する他のアプリケーション ロジックがあるため、これは悪いことです (さらに、意味的には、同じプレイヤーで同時に 2 つの異なる曲を再生することは意味がありません)。 . この更新をアトミックに行う方法はありますか? メソッドをトランザクションとして実行するだけですActivePlaylistEntry'PL''PL'on_commit_successデコレータが機能していないようです。特定のプレーヤーに属するすべての曲のテーブルをロックする方法はありますか? モデル (ブール値フィールド) に列を追加して、lockそれをスピンするか、スレッドを数ミリ秒間一時停止して再度チェックすることを考えていましたが、これらは非常にハックで汚れているように感じます。ストアド プロシージャを作成することも考えていましたが、それは実際にはデータベースに依存しません。

4

1 に答える 1

20

ロッククエリは 1.4 で追加されました。

with transaction.commit_manually():
  ActivePlayListEntry.objects.select_for_update().filter(...)
  aple = ActivePlayListEntry.objects.get(...)
  aple.state = ...
  transaction.commit()

ForeignKeyただし、「アクティブな」曲を示すために a を含む別のテーブルが使用されるように、リファクタリングを検討する必要があります。

于 2012-08-10T20:05:59.103 に答える