@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。
答案 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)