Search code examples
yesodpersistent

Given the below definition of runDB, will the second insert rollback upon failure of the first?


I'm using Yesod as the framework, postgresql as the DB, and have the following definition of runDB. Looking at the docs on the Yesod site, I have a hunch that using runDB in the following manner will cause a rollback of the first insert upon the failure of the second. Am I right. If not, how do I invoke a rollback?

instance YesodPersist App where
    <snip>
    runDB action = do
        master <- getYesod
        runSqlPool action $ appConnPool master


addKitteh :: Kitteh -> Handler (Either StoreError StoreResult)
addKitteh (Kitteh desc color size photo) = do
    data_key <- runDB $ do
                         data_key <- insert (KittehDesc desc color size)
                         insert (KittehPic data_key photo)
...

edit - Also, what happens if the first insert fails?

edit - I thought the model might be significant

KittehDesc json
   blurb Text
   color Color
   size KittehSize
   deriving Show

KittehPic
  kittehId KittehDescId Eq
  kittehPic Base64
  UniqueKittehId kittehId

Solution

  • Yes, everything in runDB is wrapped in a transaction. If the first insert fails an exception will be thrown, and the code won't reach the second insert.

    I think this is documented somewhere, but I just traced the code to come to this conclusion: runDB is implemented with defaultRunDB, which calls into runPool, which calls runSqlPool, which calls into runSqlConn, whic you can see is handling rollbacks when an exception occurs:

    runSqlConn :: MonadBaseControl IO m => SqlPersistT m a -> SqlBackend -> m a
    runSqlConn r conn = control $ \runInIO -> mask $ \restore -> do
        let getter = getStmtConn conn
        restore $ connBegin conn getter
        x <- onException
                (restore $ runInIO $ runReaderT r conn)
                (restore $ connRollback conn getter)
        restore $ connCommit conn getter
        return x