2009-06-23 71 views
7

我正在做一個Django的Web應用程序,它允許用戶建立一系列GET/POSTs之前,通過一個最終的POST提交到數據庫(或恢復)一系列的變化。我必須保持更新與任何併發數據庫用戶隔離,直到他們被確認(這是一個配置前端),在每次POST後排除提交。在Django每會話事務

我的首選解決方案是使用每會話事務。這樣可以在記錄它所屬的數據庫中記錄所發生的變化(以及它如何影響後續查詢)以及實現提交/回滾的所有問題。死鎖和長期鎖定不是問題,因爲由於外部約束,每次只能有一個用戶配置系統,並且它們表現良好。

但是,我找不到有關設置Django的ORM使用這種事務模型的文檔。我一起扔了一個最小的猴子補丁(ew!)來解決問題,但不喜歡這種脆弱的解決方案。有沒有其他人做過?我在某處錯過了一些文檔嗎?

(我的Django的版本是1.0.2決賽,和我使用的是Oracle數據庫。)

回答

9

多個併發會話大規模交易通常會導致死鎖或更糟(更糟==活鎖,長延遲,而鎖由另一個會話舉行。)

這種設計不是最好的政策,這就是爲什麼Django不鼓勵它。

更好的解決方案如下。

  1. 設計一個備忘錄類,記錄用戶的變化。這可能是其表單輸入的保存副本。如果狀態更改很複雜,則可能需要記錄其他信息。否則,表單輸入的副本可能就足夠了。

  2. 累積序列紀念品對象在他們的會話中。請注意,交易中的每個步驟都將涉及從數據中提取和驗證以查看這條記憶鏈是否仍然「有效」。有時他們不會工作,因爲別人在這條鏈條上改變了一些東西。現在怎麼辦?

  3. 當您提交'準備提交?'時頁面,你已經重播了紀念品的序列,並且非常確定它們會起作用。當提交「提交」時,你必須最後一次重放紀念品,希望他們仍然能夠工作。如果他們這樣做,很好。如果他們不這樣做,某人改變了一些事情,而你又回到了第二步:現在呢?

這似乎很複雜。

是的,它的確如此。然而,它並沒有鎖定任何鎖定,允許速度很快而且很少有機會發生死鎖。該交易僅限於「提交」視圖功能,該功能實際上將數據序列Mementos應用於數據庫,保存結果並做最後的提交以結束交易。

另一種方法 - 在用戶在步驟n-1中步出一杯快速咖啡時鎖住鎖 - 是行不通的。

欲瞭解更多關於紀念品,請參閱this

+0

死鎖和用戶出去喝咖啡並不是問題(將有一個控制器,並且通過設計,整個更新在單個鎖下完成)。 糾正我,如果我錯了,但紀實不會與ORM工作,他們會嗎? – 2009-06-23 17:37:32

1

如果任何人有過完全一樣的問題,因爲我(我希望不是),這裏是我的猴補丁。這是脆弱和醜陋的,並改變私人方法,但幸好它很小。除非你真的需要,否則請不要使用它。正如其他人所提到的,任何使用它的應用程序都會有效地防止多個用戶同時進行更新,從而導致死鎖。 (在我的應用程序,可能有很多讀者,但多個併發更新是故意除外)。

我有一個「用戶」的對象,它仍然存在跨用戶會話,幷包含一個持久連接對象。當我驗證特定的HTTP交互是會話的一部分時,我還將用戶對象存儲在線程本地的django.db.connection上。

def monkeyPatchDjangoDBConnection(): 
    import django.db 
    def validConnection(): 
     if django.db.connection.connection is None: 
      django.db.connection.connection = django.db.connection.user.connection 
     return True 
    def close(): 
     django.db.connection.connection = None 
    django.db.connection._valid_connection = validConnection 
    django.db.connection.close = close 
monkeyPatchDBConnection() 

def setUserOnThisThread(user): 
    import django.db 
    django.db.connection.user = user 

這最後在與@login_required註釋任何方法開始時自動調用,因此99%的我的代碼是從這個技巧的細節絕緣。

2

我想出了類似備忘錄模式,但不多,所以我認爲它擔負着發佈不同的東西。當用戶開始編輯會話時,我將目標對象複製到數據庫中的臨時對象。所有後續的編輯操作都會影響重複。在每次更改時,我都不會將對象狀態保存在備忘錄中,而是存儲操作對象。當我將操作應用於對象時,它會返回我存儲的反向操作。

保存操作對我來說比紀念品便宜得多,因爲操作可以通過一些小的數據項進行描述,同時正在編輯的對象要大得多。另外,我會按照我的操作來應用這些操作並保存撤銷信息,以便db中的臨時信息始終與用戶瀏覽器中的版本相對應。我從來不需要重播一系列變化;從下一個版本開始臨時只有一個操作。

要實現「撤銷」,我彈出一次撤銷的對象從堆棧(因爲它是 - 通過檢索從DB臨時對象最近的操作),它適用於臨時性,並返回轉化暫時的。如果我關心實現重做,我也可以將結果操作推送到重做堆棧上。

要實現「保存更改」,即承諾,我去激活和時間標記的原始對象並啓動臨時代替它。

要實現「取消」,即回滾,我什麼也不做!當然,我可以刪除臨時文件,因爲在編輯會話結束後用戶無法檢索它,但是我希望保留已取消的編輯會話,這樣我就可以在用cron作業清除它們之前運行統計信息。