同時アクセスに関するルールは (Haskell 側では) 文書化されておらず、開発者が使用されている特定のバックエンドに精通していると単純に想定しているようです。本番環境のニーズでは、これは完全に正当な仮定ですが、カジュアルなプロトタイピングと開発では、persistent-* パッケージがもう少し自己完結型であるとよいでしょう。
では、persistent-sqlite とファミリーへの同時アクセスを管理するルールは何ですか? 暗黙的に、接続のプールがある場合、ある程度の同時実行が許可されている必要がありますが、単純に単一の接続プールを作成して呼び出すとreplicateM x $ forkIO (useThePool connectionPool)
、次のエラーが発生します。
user error (SQLite3 returned ErrorBusy while attempting to perform step.)
編集: いくつかのサンプル コードを以下に示します。
以下のコードでは、6 つのスレッドを分岐しています (任意の数 - 私の実際のアプリケーションは 3 つのスレッドを実行します)。各スレッドは常にレコード (他のスレッドによってアクセスされているレコードからの一意のレコードですが、それは問題ではありません) を格納して検索し、フィールドの 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
注:この例では40
、 、TEST
、およびすべてのレコードの値は任意です。より現実的な値を含め、多くの値が同じ動作を引き起こします。
forever
また、 ( によって開始された) DB トランザクション内に非終了アクションを ( 経由で) ネストすると、明らかに壊れる可能性がありますがrunSqlPool
、これは主要な問題ではないことに注意してください。これらの操作を逆にして、トランザクションを任意に小さくすることができますが、それでも定期的な例外が発生します。
通常、出力は次のようになります。
$ ./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.)