32 votes

Quelles sont les règles d'accès simultané à une base de données persistante

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.)

16voto

Tom B Points 482

Quelque chose à noter est que SQLite a des problèmes avec verrouillage lorsque stockés sur NFS-comme des volumes (vboxsf, NFS, SMB, mvfs, etc.) sur de nombreux systèmes qui cause SQLite pour donner à cette erreur avant même que vous avez réussi à ouvrir la base de données. Ces volumes peuvent mettre en œuvre fcntl() lecture/écriture verrouille de manière incorrecte. ( http://www.sqlite.org/faq.html#q5 )

En supposant que ce n'est pas la question, il est également intéressant de mentionner que SQLite n'a pas vraiment d'nativement en charge simultanées "connexions" ( http://www.sqlite.org/faq.html#q6 ), car il utilise le système de fichiers verrous afin de s'assurer que les deux écritures ne se produisent pas au même moment. (Voir la section 3.0 de http://www.sqlite.org/lockingv3.html)

En supposant que tout cela est connu, vous pouvez également vérifier la version de sqlite3 vous avez à votre environnement, car certains changements à la façon dont les différentes sortes de serrures sont acquis s'est produite dans le les 3.série x: http://www.sqlite.org/sharedcache.html

Edit: Quelques informations supplémentaires à partir de l'persistent-sqlite3 bibliothèque This package includes a thin sqlite3 wrapper based on the direct-sqlite package, as well as the entire C library

"Minces" wrapper m'a fait décider de prendre un coup d'oeil pour voir juste comment fine qu'elle est; en regardant le code, il n'a pas l'air comme si la persistance wrapper a des gardes à l'encontre d'une déclaration à la piscine, à défaut, sauf le nécessaire de la garde à traduire ou à émettre de l'erreur et de l'interruption de l'exécution, même si je dois fournir la mise en garde que je ne suis pas à l'aise avec Haskell.

Il semble que vous aurez pour se prémunir contre une déclaration dans la piscine d'échouer et de tenter à nouveau, ou que vous limitez la taille du pool à l'initialisation à 1 (ce qui semble de moins que l'idéal.)

1voto

unludo Points 1673

Meilleure pratique de la javaworld (mais qui s'applique à n'importe quelle langue): ajouter une colonne version à vos tables. Pour modifier une ligne, vous devez avoir la bonne version et vous incrémenter le numéro de version. Par conséquent, si un client tente également de modifier la ligne, il est à signaler qu'il n'a pas la bonne version plus. Il obtient une nouvelle copie et essaie de nouveau avec la version correcte.

Ceci est couplé avec optimiste mise à jour stragtegy.

Ce que je suis en train d'écrire sur est intégré dans Hibernate/JPA, les normes pour le monde java.

Vous pouvez regarder ce: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/transactions.html#transactions-optimistic-manual - chapitre 11.3.1

Pas sûr que c'est lié à votre question, mais c'est bon à savoir. Les messages que vous obtenez sont "bizarre" de l'OMI. Est-il un paramètre à la sqlLite de définir un certain nombre de connexions?

Espérons que cette aide!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X