mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-18 04:51:35 +03:00
ab8369bdcf
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/4767 GitOrigin-RevId: 042fa622c6f208cf72fee40acee28e87ebcf1f67
276 lines
8.5 KiB
Haskell
276 lines
8.5 KiB
Haskell
{-# LANGUAGE TemplateHaskell #-}
|
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
|
|
-- | Postgres Connection Settings
|
|
--
|
|
-- This module contains types and combinators related to postgres connection,
|
|
-- pool, and replica related settings.
|
|
module Hasura.Backends.Postgres.Connection.Settings
|
|
( PostgresPoolSettings (..),
|
|
PostgresSourceConnInfo (..),
|
|
PostgresConnConfiguration (..),
|
|
PGClientCerts (..),
|
|
CertVar (..),
|
|
CertData (..),
|
|
SSLMode (..),
|
|
DefaultPostgresPoolSettings (..),
|
|
getDefaultPGPoolSettingIfNotExists,
|
|
defaultPostgresPoolSettings,
|
|
setPostgresPoolSettings,
|
|
pccConnectionInfo,
|
|
pccReadReplicas,
|
|
psciDatabaseUrl,
|
|
psciPoolSettings,
|
|
psciUsePreparedStatements,
|
|
psciIsolationLevel,
|
|
psciSslConfiguration,
|
|
)
|
|
where
|
|
|
|
import Control.Lens (makeLenses)
|
|
import Data.Aeson
|
|
import Data.Aeson.Casing (aesonDrop)
|
|
import Data.Aeson.TH
|
|
import Data.Bifoldable
|
|
import Data.Bifunctor
|
|
import Data.Bitraversable
|
|
import Data.Char (toLower)
|
|
import Data.Semigroup (Max (..))
|
|
import Data.Text (unpack)
|
|
import Data.Text qualified as T
|
|
import Data.Time
|
|
import Data.Time.Clock.Compat ()
|
|
import Database.PG.Query qualified as Q
|
|
import Hasura.Base.Instances ()
|
|
import Hasura.Incremental (Cacheable (..))
|
|
import Hasura.Prelude
|
|
import Hasura.RQL.Types.Common (UrlConf (..))
|
|
import Hasura.Server.Utils (parseConnLifeTime, readIsoLevel)
|
|
import Test.QuickCheck.Instances.Semigroup ()
|
|
import Test.QuickCheck.Instances.Time ()
|
|
|
|
data PostgresPoolSettings = PostgresPoolSettings
|
|
{ _ppsMaxConnections :: !(Maybe Int),
|
|
_ppsIdleTimeout :: !(Maybe Int),
|
|
_ppsRetries :: !(Maybe Int),
|
|
_ppsPoolTimeout :: !(Maybe NominalDiffTime),
|
|
_ppsConnectionLifetime :: !(Maybe NominalDiffTime)
|
|
}
|
|
deriving (Show, Eq, Generic)
|
|
|
|
instance Cacheable PostgresPoolSettings
|
|
|
|
instance Hashable PostgresPoolSettings
|
|
|
|
instance NFData PostgresPoolSettings
|
|
|
|
$(deriveToJSON hasuraJSON {omitNothingFields = True} ''PostgresPoolSettings)
|
|
|
|
instance FromJSON PostgresPoolSettings where
|
|
parseJSON = withObject "PostgresPoolSettings" $ \o ->
|
|
PostgresPoolSettings
|
|
<$> o .:? "max_connections"
|
|
<*> o .:? "idle_timeout"
|
|
<*> o .:? "retries"
|
|
<*> o .:? "pool_timeout"
|
|
<*> ((o .:? "connection_lifetime") <&> parseConnLifeTime)
|
|
|
|
data DefaultPostgresPoolSettings = DefaultPostgresPoolSettings
|
|
{ _dppsMaxConnections :: !Int,
|
|
_dppsIdleTimeout :: !Int,
|
|
_dppsRetries :: !Int,
|
|
_dppsConnectionLifetime :: !(Maybe NominalDiffTime)
|
|
}
|
|
deriving (Show, Eq)
|
|
|
|
defaultPostgresPoolSettings :: DefaultPostgresPoolSettings
|
|
defaultPostgresPoolSettings = DefaultPostgresPoolSettings 50 180 1 (Just 600)
|
|
|
|
-- Use this when you want to set only few of the PG Pool settings.
|
|
-- The values which are not set will use the default values.
|
|
setPostgresPoolSettings :: PostgresPoolSettings
|
|
setPostgresPoolSettings =
|
|
PostgresPoolSettings
|
|
{ _ppsMaxConnections = (Just $ _dppsMaxConnections defaultPostgresPoolSettings),
|
|
_ppsIdleTimeout = (Just $ _dppsIdleTimeout defaultPostgresPoolSettings),
|
|
_ppsRetries = (Just $ _dppsRetries defaultPostgresPoolSettings),
|
|
_ppsPoolTimeout = Nothing, -- @Nothing@ is the default value of the pool timeout
|
|
_ppsConnectionLifetime = _dppsConnectionLifetime defaultPostgresPoolSettings
|
|
}
|
|
|
|
-- PG Pool Settings are not given by the user, set defaults
|
|
getDefaultPGPoolSettingIfNotExists :: Maybe PostgresPoolSettings -> DefaultPostgresPoolSettings -> (Int, Int, Int)
|
|
getDefaultPGPoolSettingIfNotExists connSettings defaultPgPoolSettings =
|
|
case connSettings of
|
|
-- Atleast one of the postgres pool settings is set, then set default values to other settings
|
|
Just connSettings' ->
|
|
(maxConnections connSettings', idleTimeout connSettings', retries connSettings')
|
|
-- No PG Pool settings provided by user, set default values for all
|
|
Nothing -> (defMaxConnections, defIdleTimeout, defRetries)
|
|
where
|
|
defMaxConnections = _dppsMaxConnections defaultPgPoolSettings
|
|
defIdleTimeout = _dppsIdleTimeout defaultPgPoolSettings
|
|
defRetries = _dppsRetries defaultPgPoolSettings
|
|
|
|
maxConnections = fromMaybe defMaxConnections . _ppsMaxConnections
|
|
idleTimeout = fromMaybe defIdleTimeout . _ppsIdleTimeout
|
|
retries = fromMaybe defRetries . _ppsRetries
|
|
|
|
data SSLMode
|
|
= Disable
|
|
| Allow
|
|
| Prefer
|
|
| Require
|
|
| VerifyCA
|
|
| VerifyFull
|
|
deriving (Eq, Ord, Generic, Enum, Bounded)
|
|
|
|
instance Cacheable SSLMode
|
|
|
|
instance Hashable SSLMode
|
|
|
|
instance NFData SSLMode
|
|
|
|
instance Show SSLMode where
|
|
show = \case
|
|
Disable -> "disable"
|
|
Allow -> "allow"
|
|
Prefer -> "prefer"
|
|
Require -> "require"
|
|
VerifyCA -> "verify-ca"
|
|
VerifyFull -> "verify-full"
|
|
|
|
deriving via (Max SSLMode) instance Semigroup SSLMode
|
|
|
|
instance FromJSON SSLMode where
|
|
parseJSON = withText "SSLMode" $ \case
|
|
"disable" -> pure Disable
|
|
"allow" -> pure Allow
|
|
"prefer" -> pure Prefer
|
|
"require" -> pure Require
|
|
"verify-ca" -> pure VerifyCA
|
|
"verify-full" -> pure VerifyFull
|
|
err -> fail $ "Invalid SSL Mode " <> unpack err
|
|
|
|
newtype CertVar
|
|
= CertVar String
|
|
deriving (Show, Eq, Generic)
|
|
|
|
instance Cacheable CertVar
|
|
|
|
instance Hashable CertVar
|
|
|
|
instance NFData CertVar
|
|
|
|
instance ToJSON CertVar where
|
|
toJSON (CertVar var) = (object ["from_env" .= var])
|
|
|
|
instance FromJSON CertVar where
|
|
parseJSON = withObject "CertVar" (\o -> CertVar <$> o .: "from_env")
|
|
|
|
newtype CertData = CertData {unCert :: Text}
|
|
deriving (Show, Eq, Generic)
|
|
|
|
instance ToJSON CertData where
|
|
toJSON = String . unCert
|
|
|
|
data PGClientCerts p a = PGClientCerts
|
|
{ pgcSslCert :: Maybe a,
|
|
pgcSslKey :: Maybe a,
|
|
pgcSslRootCert :: Maybe a,
|
|
pgcSslMode :: SSLMode,
|
|
pgcSslPassword :: Maybe p
|
|
}
|
|
deriving (Show, Eq, Generic, Functor, Foldable, Traversable)
|
|
|
|
$(deriveFromJSON (aesonDrop 3 (fmap toLower)) ''PGClientCerts)
|
|
$(deriveToJSON (aesonDrop 3 (fmap toLower)) {omitNothingFields = True} ''PGClientCerts)
|
|
|
|
instance Bifunctor PGClientCerts where
|
|
bimap f g oldCerts@(PGClientCerts {pgcSslPassword}) =
|
|
let certs = oldCerts {pgcSslPassword = f <$> pgcSslPassword}
|
|
in g <$> certs
|
|
|
|
instance Bifoldable PGClientCerts where
|
|
bifoldMap f g PGClientCerts {..} =
|
|
let gs = foldMap (foldMap g) [pgcSslCert, pgcSslKey, pgcSslRootCert]
|
|
fs = foldMap f pgcSslPassword
|
|
in gs <> fs
|
|
|
|
instance Bitraversable PGClientCerts where
|
|
bitraverse f g PGClientCerts {..} =
|
|
PGClientCerts
|
|
<$> traverse g pgcSslCert
|
|
<*> traverse g pgcSslKey
|
|
<*> traverse g pgcSslRootCert
|
|
<*> pure pgcSslMode
|
|
<*> traverse f pgcSslPassword
|
|
|
|
instance (Cacheable p, Cacheable a) => Cacheable (PGClientCerts p a)
|
|
|
|
instance (Hashable p, Hashable a) => Hashable (PGClientCerts p a)
|
|
|
|
instance (NFData p, NFData a) => NFData (PGClientCerts p a)
|
|
|
|
instance ToJSON SSLMode where
|
|
toJSON = String . tshow
|
|
|
|
deriving instance Generic Q.TxIsolation
|
|
|
|
instance Cacheable Q.TxIsolation
|
|
|
|
instance NFData Q.TxIsolation
|
|
|
|
instance Hashable Q.TxIsolation
|
|
|
|
instance FromJSON Q.TxIsolation where
|
|
parseJSON = withText "Q.TxIsolation" $ \t ->
|
|
onLeft (readIsoLevel $ T.unpack t) fail
|
|
|
|
instance ToJSON Q.TxIsolation where
|
|
toJSON Q.ReadCommitted = "read-committed"
|
|
toJSON Q.RepeatableRead = "repeatable-read"
|
|
toJSON Q.Serializable = "serializable"
|
|
|
|
data PostgresSourceConnInfo = PostgresSourceConnInfo
|
|
{ _psciDatabaseUrl :: !UrlConf,
|
|
_psciPoolSettings :: !(Maybe PostgresPoolSettings),
|
|
_psciUsePreparedStatements :: !Bool,
|
|
_psciIsolationLevel :: !Q.TxIsolation,
|
|
_psciSslConfiguration :: !(Maybe (PGClientCerts CertVar CertVar))
|
|
}
|
|
deriving (Show, Eq, Generic)
|
|
|
|
instance Cacheable PostgresSourceConnInfo
|
|
|
|
instance Hashable PostgresSourceConnInfo
|
|
|
|
instance NFData PostgresSourceConnInfo
|
|
|
|
$(deriveToJSON hasuraJSON {omitNothingFields = True} ''PostgresSourceConnInfo)
|
|
$(makeLenses ''PostgresSourceConnInfo)
|
|
|
|
instance FromJSON PostgresSourceConnInfo where
|
|
parseJSON = withObject "PostgresSourceConnInfo" $ \o ->
|
|
PostgresSourceConnInfo
|
|
<$> o .: "database_url"
|
|
<*> o .:? "pool_settings"
|
|
<*> o .:? "use_prepared_statements" .!= False -- By default, preparing statements is OFF for postgres source
|
|
<*> o .:? "isolation_level" .!= Q.ReadCommitted
|
|
<*> o .:? "ssl_configuration"
|
|
|
|
data PostgresConnConfiguration = PostgresConnConfiguration
|
|
{ _pccConnectionInfo :: !PostgresSourceConnInfo,
|
|
_pccReadReplicas :: !(Maybe (NonEmpty PostgresSourceConnInfo))
|
|
}
|
|
deriving (Show, Eq, Generic)
|
|
|
|
instance Cacheable PostgresConnConfiguration
|
|
|
|
instance Hashable PostgresConnConfiguration
|
|
|
|
instance NFData PostgresConnConfiguration
|
|
|
|
$(deriveJSON hasuraJSON {omitNothingFields = True} ''PostgresConnConfiguration)
|
|
$(makeLenses ''PostgresConnConfiguration)
|