可能的django种族条件

时间:2018-05-29 17:08:14

标签: python django signals

@receiver(post_save, sender=MyRequestLog)
def steptwo_launcher(sender, instance, **kwargs):
    GeneralLogging(calledBy='MyRequestLog', logmsg='enter steptwo_launcher').save()  # remember to remove this line
    if instance.stepCode == 100:
       GeneralLogging(calledBy='MyRequestLog', logmsg='step code 100 found. launch next step').save()
       nextStep.delay(instance.requestId,False)

我想我只是看到我的代码失去了竞争条件。我的应用程序的后端更新任务1的状态,并在应该启动下一个任务时将stepCode 100写入日志。应用程序的前端轮询以将当前步骤报告给最终用户。

看来,在后端创建了一个带有stepCode 100的条目之后,前面的请求很快就出现了,if instance.stepCode == 100:从未被发现为True。 GeneralLogging仅在可疑碰撞时报告一个条目,并且不会启动nextstep。

我的问题是1)确认这是可能的,我已经怀疑了。 2)一种解决方法,因为竞争条件不会跳过nextStep。

1 个答案:

答案 0 :(得分:2)

此问题缺少一堆可能有用的信息(例如缺少代码,缺少输出),但任何形式的代码

if state == x:
    change_state
当多个控制路径命中此代码时,

存在潜在的竞争条件。

处理此问题的两种最常见方法是(1)锁定:

with some_lock:
    if state:
        change_state

即。阻止其他人在我们完成之前点击此代码,以及(2)队列:

queue.push(item_to_be_processed)

# somewhere else
item_to_be_processed = queue.pop()

db中的队列/锁实现可以使用select_for_update并使用额外的processed字段,即让" writer"使用processed = False保存模型并使用#34;读者"过程做:

from django.db import transaction
...
with transaction.atomic():
    items = MyRequestLog.objects.select_for_update(skip_locked=True).filter(
        stepCode=100, 
        processed=False
    )
    for item in items:
        do_something_useful(item)  # you might want to pull this outside of the atomic block if your application allows (so you don't keep rows locked for an extended period)
        item.processed = True
        item.save()

ps:检查数据库是否支持(https://docs.djangoproject.com/en/2.0/ref/models/querysets/#select-for-update