Python Django竞争条件:代码不获取新创建的记录
我正在运行Django(1.11.20,带有Python Django竞争条件:代码不获取新创建的记录,python,django,postgresql,Python,Django,Postgresql,我正在运行Django(1.11.20,带有原子_请求:True)和Postgres,并且有一个Django视图,基本上可以: job = Job(name='hello') job.save() files = JobFile.objects.create(job_id=job.id, file='myfile.txt') 大多数情况下,这种方法很好用。但有时,另一个进程(由cron执行)会检查JobFile表,发现它不包含给定现有作业的记录,执行以下操作时: jobs = Job.obje
原子_请求:True
)和Postgres,并且有一个Django视图,基本上可以:
job = Job(name='hello')
job.save()
files = JobFile.objects.create(job_id=job.id, file='myfile.txt')
大多数情况下,这种方法很好用。但有时,另一个进程(由cron执行)会检查JobFile
表,发现它不包含给定现有作业的记录,执行以下操作时:
jobs = Job.objects.filter(timestamp=my_timestamp)
job_files = {}
for jf in JobFile.objects.filter(job__in=jobs):
try:
job_files[jf.job_id].add(jf.file)
except KeyError:
job_files[jf.job_id] = set([jf.file])
for job in jobs:
files = job_files[job.id] if job.id in job_files else set()
print('Job {} found with files {}'.format(job.id, files))
# output when this problem occurs is typically:
# Job found 123 with files set()
它报告说它在日志中发现了一个作业
而没有作业文件
,并且出现了错误,当我稍后检查时,DB包含一个作业文件
很好
我在琢磨为什么它找不到JobFile
记录,现在我调查发现,在最近发生的这个问题中,cron进程在记录创建之前大约0.1秒启动,之后很快完成,这让我怀疑是否存在某种时间问题。但我(有限的)理解是,当这一切都在一个视图中时,原子请求将确保两个对象都存在。我的另一个疑点是create
方法,但它在根据其属性调用save之后似乎返回得很好。我错过了什么?看起来你有比赛条件。Django和Postgres使用隔离级别,这意味着您的cron作业中的查询将在提交事务后看到新对象
我已经在代码中添加了注释来解释这个问题
# This doesn't cause a query because Django querysets are lazy
jobs = Job.objects.filter(timestamp=my_timestamp)
job_files = {}
# This fetches the jobfiles for the jobs existing at this point
for jf in JobFile.objects.filter(job__in=jobs):
try:
job_files[jf.job_id].add(jf.file)
except KeyError:
job_files[jf.job_id] = set([jf.file])
# During the loop above, extra jobs and jobfiles are saved to the database
# This line causes the jobs queryset to be evaluated. It includes the new jobs
for job in jobs:
files = job_files[job.id] if job.id in job_files else set()
print('Job {} found with files {}'.format(job.id, files))
通过使用list()
强制在脚本开头计算作业查询集,可以避免此错误
jobs = list(Job.objects.filter(timestamp=my_timestamp))
你确定每个作业
都有一个作业文件
?谢谢,编辑后添加了关于无作业和无作业文件的有效点。这个解释很有道理,我从未意识到所有作业文件
都是从那里获取的。我需要一段时间才能看到没有错误发生(可能需要修复一种使此可测试的方法)。谢谢我从来没有意识到所有的作业文件都是从那里取来的——我的措辞并没有那么清楚。它获取在该点存在的匹配的jobfile
s。问题是.filter(
job\uu in=jobs)`中使用的作业与中作业的不一定相同。我确实理解了你的最后一句话,但这是否意味着作业
会被评估两次,一次在filter(job\uu in=jobs)
中,然后在迭代中再次评估,或者django是否形成某种类型的JOIN
?按照我的建议使用list()
,强制对作业
进行一次评估。在此之前,过滤器(job\uu in=jobs)
将是一个子查询,然后当您为job in job
调用时,将获取jobs
。你可以说Django在利用你自己。阅读上面的文档会很有用。