@@ -667,6 +667,15 @@ withTransactionSerializable =
667 { isolationLevel = Serializable
667 { isolationLevel = Serializable
668 , readWriteMode = ReadWrite
668 , readWriteMode = ReadWrite
670+ retryOnNotSerializable
672+ retryOnNotSerializable exception =
674+ SqlError{..} | sqlState == serialization_failure
677+ -- http://www.postgresql.org/docs/current/static/errcodes-appendix.html
678+ serialization_failure = "40001"
671-- | Execute an action inside a SQL transaction with a given isolation level.
680-- | Execute an action inside a SQL transaction with a given isolation level.
672withTransactionLevel :: IsolationLevel -> Connection -> IO a -> IO a
681withTransactionLevel :: IsolationLevel -> Connection -> IO a -> IO a
@@ -682,14 +691,15 @@ withTransactionMode mode conn act =
682 commit conn
691 commit conn
685−-- | Like 'withTransactionMode', but if a 'SqlError' arises whose 'sqlState' is
694+-- | Like 'withTransactionMode', but also takes a custom callback to
686−-- @\"40001\"@ (@serialization_failure@), this will issue a @ROLLBACK@, then
695+-- determine if a transaction should be retried if an 'SqlError' occurs.
687−-- try the action again. If any other exception arises, this will issue a
696+-- If the callback returns True, then the transaction will be retried.
688−-- @ROLLBACK@, but will propagate the exception instead of retrying.
697+-- If the callback returns False, or an exception other than an 'SqlError'
698+-- occurs then the transaction will be rolled back and the exception rethrown.
690-- This is used to implement 'withTransactionSerializable'.
700-- This is used to implement 'withTransactionSerializable'.
691−withTransactionModeRetry :: TransactionMode -> Connection -> IO a -> IO a
701+withTransactionModeRetry :: TransactionMode -> (SqlError -> Bool) -> Connection -> IO a -> IO a
692−withTransactionModeRetry mode conn act =
702+withTransactionModeRetry mode shouldRetry conn act =
693 mask $ \restore ->
703 mask $ \restore ->
694 retryLoop $ try $ do
704 retryLoop $ try $ do
695 a <- restore act
705 a <- restore act
@@ -703,16 +713,12 @@ withTransactionModeRetry mode conn act =
703 case r of
713 case r of
704 Left e -> do
714 Left e -> do
705 rollback conn
715 rollback conn
706− case fromException e of
716+ case fmap shouldRetry (fromException e) of
707− Just SqlError{..} | sqlState == serialization_failure
708− -> retryLoop act'
717+ Just True -> retryLoop act'
709− _ -> throwIO e
718+ _ -> throwIO e
710 Right a ->
719 Right a ->
713− -- http://www.postgresql.org/docs/current/static/errcodes-appendix.html
714− serialization_failure = "40001"
716-- | Rollback a transaction.
722-- | Rollback a transaction.
717rollback :: Connection -> IO ()
723rollback :: Connection -> IO ()
718rollback conn = execute_ conn "ABORT" >> return ()
724rollback conn = execute_ conn "ABORT" >> return ()