I'm working on a system which has to handle a number of race-conditions when serving jobs to a number of worker-machines.
The clients would query the system for jobs with status='0' (ToDo), then, in an atomic way, update the 'oldest' row with status='1' (Locked) and retrieve the id for that row (for updating the job with worker information like which machine is working on it etc.).
The main issue here is that there might be any number of clients updating at the same time. A solution would be to lock around 20 of the rows with status='0', update the oldest one and release all the locks again afterwards. I've been looking into the TransactionMiddleware but I don't see how this would prevent the case of the oldest one being updated from under me after I query it.
I've looked into the QuerySet.update() thing, and it looks promising, but in the case of two clients getting a hold of the same record, the status would simply updated, and we would have two workers working on the same job.. I'm really at a loss here.
I also found ticket #2705 which seems to handle the case nicely, but I have no idea how to get the code from there because of my limited SVN experience (the last updates are simply diffs, but I don't know how to merge that with the trunk of the code).
Code: Result = Job
class Result(models.Model):
"""
Result: completed- and pending runs
'ToDo': job hasn't been acquired by a client
'Locked': job has been acquired
'Paused'
"""
# relations
run = models.ForeignKey(Run)
input = models.ForeignKey(Input)
PROOF_CHOICES = (
(1, 'Maybe'),
(2, 'No'),
(3, 'Yes'),
(4, 'Killed'),
(5, 'Error'),
(6, 'NA'),
)
proof_status = models.IntegerField(
choices=PROOF_CHOICES,
default=6,
editable=False)
STATUS_CHOICES = (
(0, 'ToDo'),
(1, 'Locked'),
(2, 'Done'),
)
result_status = models.IntegerField(choices=STATUS_CHOICES, editable=False, default=0)
# != 'None' => status = 'Done'
proof_data = models.FileField(upload_to='results/',
null=True, blank=True)
# part of the proof_data
stderr = models.TextField(editable=False,
null=True, blank=True)
realtime = models.TimeField(editable=False,
null=True, blank=True)
usertime = models.TimeField(editable=False,
null=True, blank=True)
systemtime = models.TimeField(editable=False,
null=True, blank=True)
# updated when client sets status to locked
start_time = models.DateTimeField(editable=False)
worker = models.ForeignKey('Worker', related_name='solved',
null=True, blank=True)