packages feed

psql-0.0.0: lib/PostgreSQL/ConnectionPool.hs

{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

-- | This module defines an interpreter for 'Class.RunQuery'.
module PostgreSQL.ConnectionPool
  ( ConnectionPoolT (..)
  , runConnectionPoolT
  , defaultConnectionPoolSettings
  , connectionPoolMetrics
  )
where

import qualified Control.Monad.Catch as Catch
import           Control.Monad.Conc.Class (MonadConc)
import qualified Control.Monad.Except as Except
import           Control.Monad.IO.Class (MonadIO, liftIO)
import qualified Control.Monad.Reader as Reader
import           Control.Monad.State.Class (MonadState)
import           Control.Monad.Trans (MonadTrans, lift)
import           Control.Monad.Writer.Class (MonadWriter)
import qualified Database.PostgreSQL.LibPQ as PQ
import           Numeric.Natural (Natural)
import qualified PostgreSQL.Class as Class
import qualified PostgreSQL.Query as Query
import           PostgreSQL.Types (Connection)
import qualified Simpoole as Pool
import qualified Simpoole.Monad as Pool.Monad
import qualified Simpoole.Monad.Internal as Pool.Monad

-- | Interpreter for 'Class.RunQuery' which dispatches queries to a pool of database connections
--
-- @since 0.0.0
newtype ConnectionPoolT m a = ConnectionPoolT
  { unConnectionPoolT :: Pool.Monad.PoolT Connection m a }
  deriving newtype
    ( Functor -- ^ @since 0.0.0
    , Applicative -- ^ @since 0.0.0
    , Monad -- ^ @since 0.0.0
    , MonadFail -- ^ @since 0.0.0
    , MonadIO -- ^ @since 0.0.0
    , MonadState s -- ^ @since 0.0.0
    , Except.MonadError e -- ^ @since 0.0.0
    , MonadWriter w -- ^ @since 0.0.0
    , Catch.MonadThrow -- ^ @since 0.0.0
    , Catch.MonadCatch -- ^ @since 0.0.0
    , Catch.MonadMask -- ^ @since 0.0.0
    , MonadConc -- ^ @since 0.0.0
    )

-- | @since 0.0.0
instance MonadTrans ConnectionPoolT where
  lift = ConnectionPoolT . Reader.lift

-- | @since 0.0.0
instance
  (Catch.MonadMask m, MonadIO m)
  => Class.RunQuery (Query.QueryT m) (ConnectionPoolT m)
  where
    runQuery query = ConnectionPoolT $ Pool.Monad.withResource $ \conn ->
      lift $ Query.runQueryT conn query

    {-# INLINE runQuery #-}

-- | Default settings for the connection pool
--
-- @since 0.0.0
defaultConnectionPoolSettings :: Pool.Settings
defaultConnectionPoolSettings = Pool.defaultSettings
  { Pool.settings_idleTimeout = Just 60 -- seconds
  , Pool.settings_returnPolicy = Pool.ReturnToFront
  , Pool.settings_maxLiveLimit = Nothing
  }

-- | Run connection pool transformer.
--
-- @since 0.0.0
runConnectionPoolT
  :: (MonadIO m, MonadConc m)
  => m Connection
  -- ^ Action to establish a new connection
  -> Pool.Settings
  -- ^ Connection pool settings
  -> ConnectionPoolT m a
  -- ^ Transformer to run
  -> m a
runConnectionPoolT connect poolSettings (ConnectionPoolT inner) = do
  pool <- Pool.newPool connect (liftIO . PQ.finish) poolSettings
  Pool.Monad.runPoolT pool inner

{-# INLINE runConnectionPoolT #-}

-- | Retrieve the connection pool metrics.
--
-- @since 0.0.0
connectionPoolMetrics :: ConnectionPoolT m (Pool.Metrics Natural)
connectionPoolMetrics = ConnectionPoolT Pool.Monad.metricsPoolT