Il semble que les règles sur l'accès simultané sont des sans-papiers (sur le Haskell côté) et il suffit de supposer que le développeur est familier avec le particulier backend utilisé. Pour les besoins de la production c'est parfaitement légitime hypothèse, mais décontracté, le prototypage et le développement, il serait bien si la persistance de l'-* les forfaits étaient un peu plus autonome.
Alors, quelles sont les règles régissant l'accès simultané à persistants-sqlite et de la famille? Implicitement, il doit y avoir un certain degré de concurrence d'accès autorisé si nous avons des pools de connexions, mais trivialement de la création d'un pool de connexion unique et appelant replicateM x $ forkIO (useThePool connectionPool)
donne l'erreur ci-dessous.
user error (SQLite3 returned ErrorBusy while attempting to perform step.)
EDIT: un exemple de code est ci-dessous.
Dans le code ci-dessous j'ai fourche à 6 fils (un nombre arbitraire - mon application n'3 fils). Chaque thread constamment magasins et de recherche d'un dossier (un dossier unique de l'être consulté par les autres threads, mais qui n'a pas d'importance), l'impression de l'un des champs.
{-# 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 Les valeurs de 40
, TEST
,, et tous les enregistrements sont arbitraires pour cet exemple. Beaucoup de valeurs, y compris les plus réalistes, provoquer le même comportement.
À noter également que, bien qu'il puisse être cassé évidemment lorsque vous imbriquez des non-terminaison de l'action (via forever
) à l'intérieur d'un DB transaction (commencé en runSqlPool
), ce n'est pas le principal problème. Vous pouvez inverser les opérations et les transactions arbitrairement petite, mais toujours avec des exceptions.
Le résultat est généralement comme:
$ ./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.)