2012-12-06 110 views
4

摘要:有沒有在Django會話的競爭條件,以及如何預防呢?Django會話競賽狀態?

我有,我認爲涉及的競爭條件由於同一用戶的併發請求的Django會話一個有趣的問題。

它發生在同時上傳多個文件的腳本,在本地主機上進行測試。我認爲這會使得來自同一用戶的同時請求很有可能(由於本地主機的響應時間較短,文件上傳導致請求較長)。儘管如此,對於localhost之外的普通請求仍然是可能的,只是不太可能。

我派幾個(文件後)的請求,我認爲這樣做:

  1. Django的自動檢索用戶的會話*
  2. 無關的代碼需要一些時間
  3. 獲取request.session['files'](一字典)
  4. 將關於當前文件的數據追加到字典
  5. 將字典存儲在request.session['files'] a獲得
  6. 檢查確實已經存儲
  7. 更多不相關的代碼,需要時間
  8. Django的自動存儲用戶的會話

在此間舉行6.檢查將指示信息確實已存儲在會話中。但是,未來的請求表明有時它有,有時它沒有。

我認爲正在發生的是,其中兩個請求(A和B)的同時發生。請求A首先檢索request.session['files'],然後B執行相同的操作,更改並存儲它。當A終於完成後,它會覆蓋B.

會話變化

兩個問題:

  1. 這確實是發生了什麼事? django開發服務器是多線程的嗎?在Google上,我找到了關於使其成爲多線程的頁面,暗示默認情況下它不是?否則,可能是什麼問題?
  2. 如果這場比賽條件的問題,這將是解決這個問題的最好方法?這是一個不便,但不是一個安全問題,所以如果機會可以大大減少,我已經很高興。

在更改之前檢索會話數據並在之後保存它應該會顯着降低機會我認爲。但是,我還沒有找到一種方法來爲request.session做這個,只能用django.contrib.sessions.backends.db.SessionStore來解決這個問題。不過,我認爲如果我改變這種方式,Django將在請求結束時用request.session覆蓋它。

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

回答

4
  1. 是的,一個請求可能在另一個完成之前啓動。您可以通過在視圖的開始和結束處打印一些內容來檢查此問題,並同時啓動一系列請求。

  2. 的確,會話在視圖之前加載並保存在視圖之後。您可以使用request.session = engine.SessionStore(session_key)重新加載會話並使用request.session.save()進行保存。

重新加載會話但會放棄在此之前添加到會話中的任何數據(在視圖中或之前)。重新加載之前保存將會延遲加載的時間點。更好的方法是將文件作爲新模型保存到數據庫中。

答案的本質在於對托馬斯的回答的討論,該回答並不完整,因此我發佈了完整的答案。

+0

不知道爲什麼,但會話重新加載無法正常工作,即使之前有睡眠。不過,數據庫解決方案的功能非常好,值得額外購買。 – Mark

1

這是事實。您可以通過查看django.contrib.sessions.middleware.SessionMiddleware來確認。

基本上,request.session加載request擊中了你的視圖(process_request)之前,它是在會話後端(如有需要)更新後的response已經離開你的視圖(process_response)。

如果我的意思不清楚,你可能想看看the django documentation for Middleware


解決此問題的最佳方法取決於您試圖使用該信息實現的目標。如果您提供這些信息,我會更新我的答案!

+0

很明顯。我想讓Django手動重新加載和存儲會話數據,以便儘可能減少競爭條件的時間跨度。 (假設沒有簡單的方法來阻止它一起)。基本上,我想手動執行Django在查看代碼之前和之後執行的操作。 – Mark

+0

@Mark通過'request.session = engine.SessionStore(session_key)'完成加載會話,並使用'request.session.save()'保存它。根據您的會話後端,以及您是否使用事務,這可能根本不起作用。如果你想沿着這條路線走,我建議使用另一個後端而不是數據庫。 –

+0

這似乎是我正在尋找。爲什麼數據庫會話不適合這個? (請將解決方案添加到您的答案中,以便我可以接受) – Mark

1

馬克只是釘它,只有輕微的另外從我的,是如何加載該會話:

for key in session.keys(): # if you have potential removals 
    del session[key] 
session.update(session.load()) 
session.modified = False # just making it clean 

第一行可選的,你只需要它,如果某些值可能同時從會議中刪除。

最後一行是可選的,如果更新會話,那麼它並不重要。