-
-
Save snoyberg/1576120 to your computer and use it in GitHub Desktop.
| {-# LANGUAGE FlexibleContexts #-} | |
| import Database.Persist.Sqlite | |
| import Control.Concurrent.MVar | |
| import Control.Concurrent.Chan | |
| import Control.Concurrent.Lifted (fork) | |
| import Control.Monad.Base (liftBase) | |
| import Control.Monad.Trans.Control (MonadBaseControl) | |
| import Control.Monad (forever) | |
| import Control.Monad.IO.Class (MonadIO) | |
| newtype Future a = Future (MVar a) | |
| readFuture (Future m) = readMVar m | |
| newtype ActionQueue m = ActionQueue (Chan (SqlPersist m ())) | |
| startActionThread :: (MonadIO m, MonadBaseControl IO m) | |
| => Connection | |
| -> m (ActionQueue m) | |
| startActionThread conn = do | |
| chan <- liftBase newChan | |
| fork $ flip runSqlConn conn $ forever $ do | |
| action <- liftBase $ readChan chan | |
| action | |
| return $ ActionQueue chan | |
| addAction :: MonadBaseControl IO m | |
| => ActionQueue m | |
| -> SqlPersist m a | |
| -> m (Future a) | |
| addAction (ActionQueue chan) action = do | |
| mvar <- liftBase newEmptyMVar | |
| let fullAction = do | |
| x <- action | |
| liftBase $ putMVar mvar x | |
| liftBase $ writeChan chan fullAction | |
| return $ Future mvar |
Doh! I meant to put in exactly that! Of course that's a much better idea, since you don't constantly destroy and creating the same monad. Though I'm not sure how inefficient that is with ReaderT, it's definitely worth fixing. Can you mention this to Rian on the list?
What do you mean by "destroy and create"? Using forever on the outside will call runSqlConn many times instead of just once. I just thought that when an error occurs, you can't just catch the exception and continue, you need another transaction. So with a single runSqlConn it would stop working after the first DB error.
I'm completely turned around here, sorry, my comment made absolutely no sense. I'm a bit more awake now.
Yes, you're correct, we should flip the order so we get proper transaction handling and exception cleanup. I was getting at the fact that doing so is less efficient, but clearly is necessary in that case.
Why not
forever $ flip runSqlConn conn $ do?