摘要:Django会话中是否存在竞争条件,我该如何预防?
我对Django会话有一个有趣的问题,我认为由于同一用户的同时请求而导致竞争条件。
它出现在一个脚本中,用于同时上传多个文件,在localhost上进行测试。我认为这很可能同时发生来自同一用户的请求(由于localhost导致的响应时间很短,因文件上传而导致请求时间过长)。但是,在localhost之外的正常请求仍然可能,但不太可能。
我发送了几个(文件发布)请求,我认为这样做:
request.session['files']
(字典)request.session['files']
这里的检查表明该信息确实存储在会话中。但是,未来的请求表明有时它有,有时它没有。
我认为发生的是其中两个请求(A和B)同时发生。请求A首先检索request.session['files']
,然后B执行相同的操作,更改并存储它。当A终于完成时,它会用B覆盖会话更改。
两个问题:
在更改之前检索会话数据并立即保存它应该会减少我认为的机会。但是,我还没有找到一种方法为request.session
执行此操作,只能使用django.contrib.sessions.backends.db.SessionStore
解决此问题。但是我认为如果我这样改变它,Django将在请求结束时用request.session
覆盖它。
所以我基本上需要request.session.reload()
和request.session.commit()
。
答案 0 :(得分:5)
是的,请求可以在另一个请求完成之前启动。您可以通过在视图的开头和结尾打印一些内容来检查这一点,并同时启动一堆请求。
确实会话在视图之前加载并在视图之后保存。您可以使用request.session = engine.SessionStore(session_key)
重新加载会话,并使用request.session.save()
保存。
然而,重新加载会话会丢弃之前添加到会话中的任何数据(在视图中或之前)。在重新加载之前保存会延迟加载点。更好的方法是将文件作为新模型保存到数据库中。
答案的实质在于托马斯答案的讨论,这个答案是不完整的,所以我发布了完整的答案。
答案 1 :(得分:2)
马克只是把它钉了起来,只是我的一点点补充,就是如何加载那个会话:
for key in session.keys(): # if you have potential removals
del session[key]
session.update(session.load())
session.modified = False # just making it clean
第一行是可选的,如果同时从会话中移除某些值,则只需要它。
最后一行是可选的,如果您更新会话,那么它并不重要。
答案 2 :(得分:1)
这是事实。您可以查看django.contrib.sessions.middleware.SessionMiddleware
。
基本上,request.session
会在request
点击你的观点之前加载response
,并在{{1}}之后在会话后端(如果需要)更新离开了你的观点(在process_request
中)。
如果我的意思不清楚,您可能需要查看process_response
。
解决问题的最佳方法取决于您尝试使用该信息实现的目标。如果你提供这些信息,我会更新我的答案!