ВОПРОС 2: Я никогда не использовал HDBC, но, вероятно, напишу что-то вроде этого.
trySql :: Connection -> (Connection -> IO a) -> IO a
trySql conn f = handleSql catcher $ do
r <- f conn
commit conn
return r
where catcher e = rollback conn >> throw e
Откройте Connection
где-нибудь за пределами функции и не отключайте его внутри функции.
ВОПРОС 1: Хм, пул соединений не кажется таким сложным для реализации ...
import Control.Concurrent
import Control.Exception
data Pool a =
Pool { poolMin :: Int, poolMax :: Int, poolUsed :: Int, poolFree :: [a] }
newConnPool low high newConn delConn = do
cs <- handleSqlError . sequence . replicate 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 (mPool, newConn, delConn) conn = modifyMVar_ mPool $ \pool ->
let used = poolUsed pool in
if used > poolMin conn
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 conPool)
Вы, вероятно, не должны принимать это дословно, поскольку я даже не тестировал его компиляцией (и fail
там довольно недружелюбно), но идея в том, чтобы сделать что-то вроде
connPool <- newConnPool 0 50 (connectSqlite3 "user.db") disconnect
и передайте connPool
вокруг, если необходимо.