2013-08-22 29 views
4

我在play的Action中編寫了下面的包裝器,這個包裝器將使用一個同時需要會話和請求的函數。這是第一個版本:爲什麼下面的代碼泄漏一個Slick數據庫集合?

def ActionWithSession[A](bp: BodyParser[A])(f: Session => Request[A] => Result): Action[A] = 
    Action(bp) { 
    db.withSession { 
     session: DbSession => 
     request => f(session)(request) 
    } 
    } 

這個版本效果很好(正確的結果返回到瀏覽器),但每次調用會泄漏一個數據庫連接。幾個電話後,我開始下列情況除外:

java.sql.SQLException: Timed out waiting for a free available connection. 

當我將其更改爲下面的版本(由行動後立即移動request =>,連接泄漏消失了,和它的作品

def ActionWithSession[A](bp: BodyParser[A])(f: Session => Request[A] => Result): Action[A] = 
    Action(bp) { request => 
    db.withSession { 
     session: DbSession => 
     f(session)(request) 
    } 
    } 

爲什麼第一個版本造成​​的連接泄漏,而第二個版本如何修復?

回答

4

該代碼的第一個版本不應該工作。您不應該從withSession範圍返回任何持有Session對象引用的東西。在這裏,你返回一個封閉的持有這樣的參考。當稍後由Play調用閉包時,withSession作用域已關閉,Session對象無效。無可否認,泄漏會話對象在閉包過程中很容易發生(並且將來會被Slick抓住)。

這就是爲什麼它起初工作,但泄漏連接:會話對象獲得連接懶惰。如果已經獲取了塊,則會話塊返回(或關閉)塊末尾的連接。當你從塊中泄漏一個未使用的Session對象並在塊結束後第一次使用它時,它仍然懶惰地打開連接,但沒有任何東西會自動關閉它。我們剛認識到這是不受歡迎的行爲,但尚未解決。我們想到的修正是禁止Session對象在調用.close方法後獲取連接。在你的情況下,這將導致異常,而不是泄漏的連接。

https://github.com/slick/slick/pull/107

正確的代碼確實是您發佈的第二個版本,其中返回封閉的身體包含整個withSession塊,不只是它的結果。

+0

感謝您的解釋!這確實是一個容易犯的錯誤,也是一個棘手的錯誤。 – thesamet

+0

你可能會對http://stackoverflow.com/questions/18579684/play-slick-and-async-is-it-a-race-condition感興趣,我認爲play-slick返回一個Future,它擁有對來自withSession塊的會話。 – thesamet

3

db.withSession收到獲取它的第一個參數會話和與它提供給它的一些會議執行它的功能。返回v db.withSession的alue是函數返回的內容。

在第一版本中,傳遞給withSession表達式的計算結果爲功能request => f(session)(request),所以db.withSession結束:實例的會話,實例綁定到該會話的功能對象,關閉會話(在它實例化的函數被調用之前),並返回這個綁定函數。現在,Action得到了正是它想要的 - 一個函數,需要Request[A]並給出Result。但是,在Play執行此操作時,會話將被延遲打開,但沒有任何操作會將其返回到池中。

第二個版本沒錯,在db.withSession中我們實際上調用了f,而不是返回一個調用f的functin。這確保了對f的調用嵌套在db.withSession中,並在會話被獲取時發生。

希望這可以幫助別人!

+0

差不多。它並沒有返回到游泳池,因爲它從來沒有被收購。 – cvogt

相關問題