Django会话竞争条件?

时间:2012-12-06 16:33:24

标签: python django multithreading session race-condition

摘要:Django会话中是否存在竞争条件,我该如何预防?

我对Django会话有一个有趣的问题,我认为由于同一用户的同时请求而导致竞争条件。

它出现在一个脚本中,用于同时上传多个文件,在localhost上进行测试。我认为这很可能同时发生来自同一用户的请求(由于localhost导致的响应时间很短,因文件上传而导致请求时间过长)。但是,在localhost之外的正常请求仍然可能,但不太可能。

我发送了几个(文件发布)请求,我认为这样做:

  1. Django会自动检索用户的会话*
  2. 需要一些时间的无关代码
  3. 获取request.session['files'](字典)
  4. 将有关当前文件的数据附加到词典
  5. 将字典再次存储在request.session['files']
  6. 检查确实已存储
  7. 需要时间的更多无关代码
  8. Django自动存储用户的会话
  9. 这里的检查表明该信息确实存储在会话中。但是,未来的请求表明有时它有,有时它没有。

    我认为发生的是其中两个请求(A和B)同时发生。请求A首先检索request.session['files'],然后B执行相同的操作,更改并存储它。当A终于完成时,它会用B覆盖会话更改。

    两个问题:

    1. 这确实发生了什么事吗? django开发服务器是多线程的吗?在Google上,我发现有关使其成为多线程的网页,建议默认情况下不是这样吗?否则,可能是什么问题?
    2. 如果这种竞争条件是问题,那么解决它的最佳方法是什么?这是一个不方便,但不是安全问题,所以如果机会可以显着减少,我已经很高兴。
    3. 在更改之前检索会话数据并立即保存它应该会减少我认为的机会。但是,我还没有找到一种方法为request.session执行此操作,只能使用django.contrib.sessions.backends.db.SessionStore解决此问题。但是我认为如果我这样改变它,Django将在请求结束时用request.session覆盖它。

      所以我基本上需要request.session.reload()request.session.commit()

3 个答案:

答案 0 :(得分:5)

  1. 是的,请求可以在另一个请求完成之前启动。您可以通过在视图的开头和结尾打印一些内容来检查这一点,并同时启动一堆请求。

  2. 确实会话在视图之前加载并在视图之后保存。您可以使用request.session = engine.SessionStore(session_key)重新加载会话,并使用request.session.save()保存。

  3. 然而,重新加载会话会丢弃之前添加到会话中的任何数据(在视图中或之前)。在重新加载之前保存会延迟加载点。更好的方法是将文件作为新模型保存到数据库中。

    答案的实质在于托马斯答案的讨论,这个答案是不完整的,所以我发布了完整的答案。

答案 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


解决问题的最佳方法取决于您尝试使用该信息实现的目标。如果你提供这些信息,我会更新我的答案!