Dans Django, j'ai rencontré des conditions de course sérieuses. Le problème commence lorsque deux coureurs essaient d'exécuter some_method() en même temps. L'exploitation forestière créée est la suivante:Condition de course dans Django
Job 3: Candidate
Job 3: Already taken
Job 3: Candidate
Job 3: Already taken
Job 3: Candidate
Job 3: Already taken
(et cetera for 18 MB)
La méthode suivante me donne du mal. Il convient de noter que la méthode est ré-ran jusqu'à ce que la méthode retourne False
:
def some_method():
conditions = #(amongst others, excludes jobs with status EXECUTING)
try:
cjob = Job.objects.filter(conditions).order_by(some_fields)[0]
except IndexError:
return False
print 'Job %s: Candidate' % cjob.id
job = cjob.for_update()
if cjob.status != job.status:
print 'Job %s: Already taken' % cjob.id
return True
print 'Job %s: Starting...' % job.id
job.status = Job.EXECUTING
job.save()
# Critical section
# In models.py:
class Job(models.Model):
# ...
def for_update(self):
return Job.objects.raw('SELECT * FROM `backend_job` WHERE `id` = %s FOR UPDATE', (self.id,))[0]
Actuellement, Django ne dispose pas d'une méthode for_update dédiée et pour empêcher la création de la requête avec toutes les conditions que nous utilisons pour déterminez si le travail doit être exécuté, nous effectuons la requête difficile avant la requête FOR UPDATE simple.
Je ne vois pas vraiment comment cela pourrait causer le problème que nous voyons, nous faisons la requête, suivie par une instruction qui bloque quand un autre coureur détient le verrou sur le travail. Le verrou n'est libéré qu'une fois l'état du travail modifié. Le deuxième coureur obtient maintenant le verrou, mais le statut du travail a été changé, il revient donc de la méthode, seulement pour le ré-entrer plus tard; mais la requête cjob
ne retournera pas le même travail, car son statut est maintenant exclu par le filtre. Est-ce que j'interprète mal la clause FOR UPDATE, ou est-ce que je manque quelque chose d'autre?
Il est à noter que j'utilise MySQL avec InnoDB et que Celery ne correspond pas à cette solution.
? Quels sont les types? – dappawit
Ce sont des caractères uniques: Job.EXECUTING == 'E'. La requête à la ligne 5 exclut les travaux avec le statut «E» (en fait, il n'accepte que ceux avec «W» ou «N»). – ralphje