似乎關於併發訪問的規則沒有記錄(在Haskell方面),並且簡單地假設開發人員熟悉正在使用的特定後端。對於生產需求來說,這是一個完全合理的假設,但對於休閒原型和開發來說,如果persistent-*包更具自包含性,那將是非常好的。關於併發訪問持久性數據庫的規則是什麼
那麼,控制併發訪問persistent-sqlite和family的規則是什麼?隱含地說,如果我們有連接池,則必須有某種程度的併發性,但是創建一個連接池並調用replicateM x $ forkIO (useThePool connectionPool)
會給出下面的錯誤。
user error (SQLite3 returned ErrorBusy while attempting to perform step.)
編輯:一些示例代碼現在在下面。
在下面的代碼中,我分離了6個線程(任意數字 - 我的實際應用程序確實有3個線程)。每個線程都會不斷存儲和查找記錄(來自其他線程訪問的記錄的唯一記錄,但這並不重要),打印其中一個字段。
{-# LANGUAGE TemplateHaskell, QuasiQuotes
, TypeFamilies, FlexibleContexts, GADTs
, OverloadedStrings #-}
import Control.Concurrent (forkIO, threadDelay)
import Database.Persist
import Database.Persist.Sqlite hiding (get)
import Database.Persist.TH
import Control.Monad
import Control.Monad.IO.Class
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
SomeData
myId Int
myData Double
MyId myId
|]
main = withSqlitePool "TEST" 40 $ \pool -> do
runSqlPool (runMigration migrateAll) pool
mapM_ forkIO [runSqlPool (dbThread i) pool | i <- [0..5]]
threadDelay maxBound
dbThread :: Int -> SqlPersist IO()
dbThread i = forever $ do
x <- getBy (MyId i)
insert (SomeData i (fromIntegral i))
liftIO (print x)
liftIO (threadDelay 100000) -- Just to calm down the CPU,
-- not needed for demonstrating
-- the problem
NB的所有記錄中的40
,TEST
值,並且是任意的這個例子。許多價值觀,包括更現實的價值觀,都會導致相同的行爲。
另請注意,雖然在嵌套數據庫事務(由runSqlPool
開始)中的非終止操作(通過forever
)時它可能會明顯中斷,但這不是核心問題。您可以反轉這些操作並使交易任意小,但仍然會有定期例外。
的輸出通常是這樣的:
$ ./so
Nothing
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorConstraint while attempting to perform step.)
你可以提供關於'useThePool'的更多細節嗎? – 2012-01-29 23:03:23
@丹伯頓我已經通過編輯提供了更多信息。現在使用示例代碼,對於那些對持久性有所瞭解的人可能是盲目的錯誤。 – 2012-01-30 01:31:58
@ ThomasM.DuBuisson,您是否嘗試過使用select而不是insert,並查看是否可以重現該錯誤?如果在select上沒有發生該錯誤,那麼該錯誤可能是某處引發的死鎖預防異常,尤其是在您嘗試執行併發插入時。沒有意義,但如果你有一個線程池。 – Sal 2012-01-30 01:52:27