这样做的第一个实例的原子更新的一个QuerySet
题
我的工作具有服务工作的若干工人的机器时,处理大量的比赛条件的系统上。
在客户端将查询系统用于与状态的作业=“0”(待办事项),然后,在一个原子的方式,更新“最老的”行与状态=“1”(锁定)和检索的ID该行(对于具有象该机器工作在其上等等。工人信息更新作业)。
这里的主要问题是,有可能是任何数量的客户端在同一时间更新的。一个解决办法是用状态=“0”,更新历史最悠久的一个,之后再次释放所有的锁锁行的约20。我一直在寻找进入TransactionMiddleware,但我看不出这会妨碍一个最老的情况下,从下我更新后,我查询了。
我进去看了QuerySet.update()的事情,它看起来很有希望,但在两个客户端得到相同的记录的保持的情况下,状态只会更新,我们将有两个工人工作同样的工作。我不知所措我真的在这里。
我还发现票#2705 这似乎很好地处理这种情况,但我有不知道怎么弄的,因为我有限的经验SVN从那里的代码(最后更新是简单的diff,但我不知道如何合并与代码的主干)。
代码:结果=作业
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)
解决方案
要合并#2705到你的Django,你需要先下载它:
cd <django-dir>
wget http://code.djangoproject.com/attachment/ticket/2705/for_update_11366_cdestigter.diff?format=raw
然后倒带SVN到必要的django版本:
svn update -r11366
然后应用它:
patch -p1 for_update_11366_cdestigter.diff
它会告诉你哪些文件被成功修补,哪些不是。在冲突的可能性不大的情况下就可以解决这些问题手动看着http://code.djangoproject.com/attachment/ticket/2705/for_update_11366_cdestigter.diff
要取消应用补丁,只写
svn revert --recursive .
其他提示
如果您的Django是一台机器上运行,还有一个更简单的做...对不起伪代码作为你的实现细节并不明确的方式。
from threading import Lock
workers_lock = Lock()
def get_work(request):
workers_lock.acquire()
try:
# Imagine this method exists for brevity
work_item = WorkItem.get_oldest()
work_item.result_status = 1
work_item.save()
finally:
workers_lock.release()
return work_item
您有两个选择了我的头顶。一是检索时立即锁定行,只有当相应的一个已经被标记为使用解除锁定。这里的问题是,没有其他客户端进程甚至可以看看哪个没有获选的作业。如果你永远都只是自动选择最后一个则可能是窗户的简要足以成为O.K.为您服务。
在另一个选项是带回那些在查询时打开行,但随后再次检查每当客户端尝试抢工作与工作。当客户端尝试更新工作在其上工作的检查将第一次做,看它是否仍然可用。如果其他人已经抓住了它,然后通知将被发送回客户端。这让所有的客户端看到所有的工作快照的,但如果他们都在不断抓住最新的一个,那么你可能有客户不断收到通知,一个工作已在使用。也许这就是你指的是哪个种族的条件?
来解决,这将是在特定群体的就业恢复到客户端,以便它们并不总是得到相同的列表中的一种方式。例如,打破他们的地理区域,甚至只是随机。例如,每个客户可以有0至9的ID以对工作的ID的MOD和发回这些作业具有相同的结局数字提供给客户。不要将其限制为仅尽管这些工作,因为你不希望这是工作,你也无法到达。因此,举例来说,如果你有1,2和3的客户和104工作那么没有人能够得到它。所以,一旦没有足够的就业机会,正确的数字结尾的工作将开始来与其他数字回来正好填补了这个名单。你可能需要在这里玩的精确算法,但希望这给你出个主意。
您如何才能对其进行更新和/或发回将在很大程度上取决于你的RDBMS通知锁定在数据库中的行。在MS SQL Server,你可以换所有的工作很好的在存储过程中,只要不需要在它的中间用户干预。
我希望这有助于。