並行アクセスに関するルールは(Haskell側で)文書化されておらず、開発者が使用されている特定のバックエンドに精通していると仮定します。本番用の場合、これは完全に正当な仮定ですが、カジュアルなプロトタイピングと開発のためには、persistent- *パッケージがもう少し自己完結型であればいいでしょう。永続データベースへの同時アクセスに関する規則
したがって、永続的なsqliteおよびファミリへの同時アクセスを制御するルールは何ですか?暗黙のうちに、接続のプールを持っているのにある程度の並列性が許されている必要がありますが、単一の接続プールを作成してreplicateM x $ forkIO (useThePool connectionPool)
を呼び出すと、以下のエラーが発生します。
user error (SQLite3 returned ErrorBusy while attempting to perform step.)
編集:いくつかのコード例を以下に示します。
以下のコードでは、6つのスレッド(任意の数 - 私の実際のアプリケーションは3つのスレッドを行います)をforkします。各スレッドは常にレコードを格納して検索します(他のスレッドからアクセスされたレコードからの一意のレコードですが、それは問題ではありません)。フィールドの1つを印刷します。
{-# 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
、およびすべてのレコードは、この例のために任意です。より現実的な値を含む多くの値は、同じ動作を引き起こします。
DBトランザクション(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'についてもっと詳しく説明できますか? –
@ダンブルトン私は編集によってさらに情報を与えました。さて、永続的なものについて何か知っている人にはおそらく盲目的に間違っているサンプルコードがあります。 –
@ ThomasM.DuBuisson、あなたはinsertの代わりにselectを使ってみましたが、エラーを再現できるかどうか確認してください。 selectでエラーが発生しない場合は、特に同時挿入を試みている場合は、どこかにスローされたデッドロック防止例外が原因の可能性があります。あなたがスレッドプールを持っている場合でも意味をなさない。 – Sal