10
votes

Piscine de connexion DB simultanée à Haskell

Je suis un programmeur Java qui apprends Haskell.
Je travaille sur une petite application Web qui utilise Happstack et parle à une base de données via HDBC.

J'ai écrit SELECT STRY> et EXEC STROND> FONCTIONS ET je les utilise comme ceci : P>

exec :: String -> [SqlValue] -> IO Integer
exec query params = withDb $ \c -> run c query params

select :: String -> [SqlValue] -> IO [[SqlValue]]
select query params = withDb $ \c -> quickQuery' c query params

withDb :: (Connection -> IO a) -> IO a
withDb f = do
    conn <- handleSqlError $ connectSqlite3 "users.db"
    catchSql
        (do r <- f conn
            commit conn
            disconnect conn
            return r)
        (\e@(SqlError _ _ m) -> do
            rollback conn
            disconnect conn
            throw e)


2 commentaires

Je n'ai pas de réponse complète pour vous, mais votre problème est que vous avez abstraité de manière incorrecte la connexion. Vous voulez probablement le mettre dans une structure ressemblant à un lecteur, de sorte qu'elle puisse être transmise à chaque requête.


HMM, SQL Les opérations sont toutes bloquées dans le io monad, donc peut-être regretière io ? Semble raisonnable.


3 Réponses :


10
votes

question 2: strong> Je n'ai jamais utilisé HDBC, mais j'écrirais probablement quelque chose comme ça. XXX PRE>

Ouvrir la connexion Code > Quelque part en dehors de la fonction et ne le déconnectez pas dans la fonction. P>

Question 1: strong> HMM, un pool de connexion ne semble pas si difficile à mettre en œuvre ... P>

connPool <- newConnPool 0 50 (connectSqlite3 "user.db") disconnect


3 commentaires

Frais! Est-ce que c'est le fil sûr? Est-il correct de créer un seul "connpool" et l'utiliser dans tous les gestionnaires de fichiers?


Il devrait s'agir de thread-coffre-fort, tout le travail est effectué dans ModifyMvar (qui est Takemvar + putmvar ), qui séquence efficacement tout le Prendre / mettre les opérations . Mais vous devez vraiment vérifier ce code vous-même, pour voir s'il convient à vos besoins.


Avant d'utiliser le test de la piscine Comment votre pilote de base de données s'échappe avec des déconnectes. J'ai essayé d'utiliser cette mise en œuvre de cette piscine avec le pilote HDBC-ODBC contre MS SQL Server. Ça fonctionne bien. Mais ensuite, j'arrête SQL Server, essayez l'application, ce qui me donne évidemment l'erreur, puis démarrez SQL Server Retour et essayez à nouveau l'application. Cela donne toujours une erreur. Malheureusement, se déconnectent sur le réseau se produisent. Assurez-vous donc de gérer des connexions défectueuses et de paver de nouvelles.



1
votes

J'ai modifié le code ci-dessus, il est maintenant capable de compiler au moins.

module ConnPool ( newConnPool, withConn, delConnPool ) where

import Control.Concurrent
import Control.Exception
import Control.Monad (replicateM)
import Database.HDBC

data Pool a =
    Pool { poolMin :: Int, poolMax :: Int, poolUsed :: Int, poolFree :: [a] }

newConnPool :: Int -> Int -> IO a -> (a -> IO ()) -> IO (MVar (Pool a), IO a, (a -> IO ()))
newConnPool low high newConn delConn = do
--    cs <- handleSqlError . sequence . replicate low newConn
    cs <- replicateM low newConn 
    mPool <- newMVar $ Pool low high 0 cs 
    return (mPool, newConn, delConn)

delConnPool (mPool, newConn, delConn) = do
    pool <- takeMVar mPool
    if length (poolFree pool) /= poolUsed pool
      then putMVar mPool pool >> fail "pool in use"
      else mapM_ delConn $ poolFree pool

takeConn (mPool, newConn, delConn) = modifyMVar mPool $ \pool ->
    case poolFree pool of
        conn:cs ->
            return (pool { poolUsed = poolUsed pool + 1, poolFree = cs }, conn)
        _ | poolUsed pool < poolMax pool -> do
            conn <- handleSqlError newConn
            return (pool { poolUsed = poolUsed pool + 1 }, conn)
        _ -> fail "pool is exhausted"

putConn :: (MVar (Pool a), IO a, (a -> IO b)) -> a -> IO ()
putConn (mPool, newConn, delConn) conn = modifyMVar_ mPool $ \pool ->
    let used = poolUsed pool in
    if used > poolMin pool
    then handleSqlError (delConn conn) >> return (pool { poolUsed = used - 1 })
    else return $ pool { poolUsed = used - 1, poolFree = conn : (poolFree pool) }

withConn connPool = bracket (takeConn connPool) (putConn connPool)


0 commentaires

21
votes

Le Package de ressource-piscine fournit un pool de ressources hautes performances pouvant être utilisé pour la mise en commun de la connexion de base de données. Par exemple:

import Data.Pool (createPool, withResource)

main = do
    pool <- createPool newConn delConn 1 10 5
    withResource pool $ \conn -> doSomething conn


1 commentaires

Je viens d'utiliser (et que je suis aimant) data.conuit.pool (paquet de conduits de pool). C'est un wrapper autour de données.pool (utilisé par Yesod et d'autres personnes) Hackage.hakell. org / colis / piscine-conduit-0.1.1