module Foundation.DB ( runDBRead , runSqlPoolRetry ) where import Import.NoFoundation hiding (runDB, getDBRunner) import Foundation.Type import qualified Control.Retry as Retry import GHC.IO.Exception (IOErrorType(OtherError)) import Database.Persist.Sql (runSqlPool, SqlReadBackend(..)) runSqlPoolRetry :: forall m a backend. ( MonadUnliftIO m, BackendCompatible SqlBackend backend , MonadLogger m, MonadMask m ) => ReaderT backend m a -> Pool backend -> m a runSqlPoolRetry action pool = do let policy = Retry.fullJitterBackoff 1e3 & Retry.limitRetriesByCumulativeDelay 10e6 handlers = Retry.skipAsyncExceptions `snoc` Retry.logRetries suggestRetry logRetry where suggestRetry :: IOException -> m Bool suggestRetry ioExc = return $ ioeGetErrorType ioExc == OtherError && ioeGetLocation ioExc == "libpq" logRetry :: forall e. Exception e => Bool -- ^ Will retry -> e -> Retry.RetryStatus -> m () logRetry shouldRetry@False err status = $logErrorS "runSqlPoolRetry" . pack $ Retry.defaultLogMsg shouldRetry err status logRetry shouldRetry@True err status = $logWarnS "runSqlPoolRetry" . pack $ Retry.defaultLogMsg shouldRetry err status Retry.recovering policy handlers $ \Retry.RetryStatus{..} -> do $logDebugS "runSqlPoolRetry" $ "rsIterNumber = " <> tshow rsIterNumber runSqlPool action pool runDBRead :: ReaderT SqlReadBackend (HandlerFor UniWorX) a -> (HandlerFor UniWorX) a runDBRead action = do $logDebugS "YesodPersist" "runDBRead" runSqlPoolRetry (withReaderT SqlReadBackend action) . appConnPool =<< getYesod