auto-close database connections when MVar is GCed
This commit is contained in:
parent
6d38f54db4
commit
d43ac8056b
1 changed files with 26 additions and 15 deletions
|
@ -26,7 +26,7 @@ import Control.Monad
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Monad.IO.Class (liftIO)
|
||||||
import Control.Concurrent
|
import Control.Concurrent
|
||||||
import Control.Concurrent.Async
|
import Control.Concurrent.Async
|
||||||
import Control.Exception (throwIO)
|
import Control.Exception (throwIO, BlockedIndefinitelyOnMVar(..))
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Control.Monad.Trans.Resource (runResourceT)
|
import Control.Monad.Trans.Resource (runResourceT)
|
||||||
import Control.Monad.Logger (runNoLoggingT)
|
import Control.Monad.Logger (runNoLoggingT)
|
||||||
|
@ -68,6 +68,8 @@ openDb db tablename = do
|
||||||
worker <- async (workerThread (T.pack db) tablename jobs)
|
worker <- async (workerThread (T.pack db) tablename jobs)
|
||||||
return $ DbHandle worker jobs
|
return $ DbHandle worker jobs
|
||||||
|
|
||||||
|
{- This is optional; when the DbHandle gets garbage collected it will
|
||||||
|
- auto-close. -}
|
||||||
closeDb :: DbHandle -> IO ()
|
closeDb :: DbHandle -> IO ()
|
||||||
closeDb (DbHandle worker jobs) = do
|
closeDb (DbHandle worker jobs) = do
|
||||||
putMVar jobs CloseJob
|
putMVar jobs CloseJob
|
||||||
|
@ -123,29 +125,38 @@ data Job
|
||||||
| CloseJob
|
| CloseJob
|
||||||
|
|
||||||
workerThread :: T.Text -> TableName -> MVar Job -> IO ()
|
workerThread :: T.Text -> TableName -> MVar Job -> IO ()
|
||||||
workerThread db tablename jobs = catchNonAsync (run loop) showerr
|
workerThread db tablename jobs =
|
||||||
|
catchNonAsync (runSqliteRobustly tablename db loop) showerr
|
||||||
where
|
where
|
||||||
showerr e = liftIO $ hPutStrLn stderr $
|
showerr e = hPutStrLn stderr $
|
||||||
"sqlite worker thread crashed: " ++ show e
|
"sqlite worker thread crashed: " ++ show e
|
||||||
|
|
||||||
|
getjob :: IO (Either BlockedIndefinitelyOnMVar Job)
|
||||||
|
getjob = try $ takeMVar jobs
|
||||||
|
|
||||||
loop = do
|
loop = do
|
||||||
job <- liftIO $ takeMVar jobs
|
job <- liftIO getjob
|
||||||
case job of
|
case job of
|
||||||
QueryJob a -> a >> loop
|
-- Exception is thrown when the MVar is garbage
|
||||||
|
-- collected, which means the whole DbHandle
|
||||||
|
-- is not used any longer. Shutdown cleanly.
|
||||||
|
Left BlockedIndefinitelyOnMVar -> return ()
|
||||||
|
Right CloseJob -> return ()
|
||||||
|
Right (QueryJob a) -> a >> loop
|
||||||
-- change is run in a separate database connection
|
-- change is run in a separate database connection
|
||||||
-- since sqlite only supports a single writer at a
|
-- since sqlite only supports a single writer at a
|
||||||
-- time, and it may crash the database connection
|
-- time, and it may crash the database connection
|
||||||
ChangeJob a -> liftIO (a run) >> loop
|
Right (ChangeJob a) -> liftIO (a (runSqliteRobustly tablename db)) >> loop
|
||||||
CloseJob -> return ()
|
|
||||||
|
|
||||||
-- like runSqlite, but calls settle on the raw sql Connection.
|
-- like runSqlite, but calls settle on the raw sql Connection.
|
||||||
run a = do
|
runSqliteRobustly :: TableName -> T.Text -> (SqlPersistM a) -> IO a
|
||||||
conn <- Sqlite.open db
|
runSqliteRobustly tablename db a = do
|
||||||
settle conn
|
conn <- Sqlite.open db
|
||||||
runResourceT $ runNoLoggingT $
|
settle conn
|
||||||
withSqlConn (wrapConnection conn) $
|
runResourceT $ runNoLoggingT $
|
||||||
runSqlConn a
|
withSqlConn (wrapConnection conn) $
|
||||||
|
runSqlConn a
|
||||||
|
where
|
||||||
-- Work around a bug in sqlite: New database connections can
|
-- Work around a bug in sqlite: New database connections can
|
||||||
-- sometimes take a while to become usable; select statements will
|
-- sometimes take a while to become usable; select statements will
|
||||||
-- fail with ErrorBusy for some time. So, loop until a select
|
-- fail with ErrorBusy for some time. So, loop until a select
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue