module Utils.Sql ( setSerializable ) where import ClassyPrelude.Yesod import Database.Persist.Sql import Database.PostgreSQL.Simple (SqlError(SqlError), sqlErrorHint) import Control.Monad.Catch (handleIf) import Data.Time.Clock setSerializable :: (MonadLogger m, MonadCatch m, MonadBase IO m, MonadIO m) => ReaderT SqlBackend m a -> ReaderT SqlBackend m a setSerializable act = setSerializable' (0 :: Integer) where act' = [executeQQ|SET TRANSACTION ISOLATION LEVEL SERIALIZABLE|] *> act setSerializable' (min 10 -> logBackoff) = handleIf (\SqlError{sqlErrorHint} -> "The transaction might succeed if retried." `isInfixOf` sqlErrorHint) (\e -> do let delay :: NominalDiffTime delay = 1e-3 * 2 ^ logBackoff $logWarnS "Sql" $ tshow (delay, e) transactionUndo threadDelay . round $ delay * 1e6 setSerializable' (succ logBackoff) ) act'