refactor: move AppState type and update functions to AppState/Types.hs
`AppState.hs` is imported by many modules. Some of those modules contain important functions that need to be imported back to `AppState.hs`, causing circular dependency problem. Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
This commit is contained in:
@@ -46,6 +46,7 @@ library
|
|||||||
exposed-modules: PostgREST.Admin
|
exposed-modules: PostgREST.Admin
|
||||||
PostgREST.App
|
PostgREST.App
|
||||||
PostgREST.AppState
|
PostgREST.AppState
|
||||||
|
PostgREST.AppState.Types
|
||||||
PostgREST.Auth
|
PostgREST.Auth
|
||||||
PostgREST.Auth.Jwt
|
PostgREST.Auth.Jwt
|
||||||
PostgREST.Auth.JwtCache
|
PostgREST.Auth.JwtCache
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ import PostgREST.Version (prettyVersion)
|
|||||||
import Control.AutoUpdate (defaultUpdateSettings, mkAutoUpdate, updateAction)
|
import Control.AutoUpdate (defaultUpdateSettings, mkAutoUpdate, updateAction)
|
||||||
import Control.Retry (RetryPolicy, RetryStatus (..), capDelay,
|
import Control.Retry (RetryPolicy, RetryStatus (..), capDelay,
|
||||||
exponentialBackoff, retrying, rsPreviousDelay)
|
exponentialBackoff, retrying, rsPreviousDelay)
|
||||||
import Data.IORef (IORef, atomicWriteIORef, newIORef, readIORef)
|
import Data.IORef (newIORef, readIORef)
|
||||||
import Data.Time.Clock (UTCTime, getCurrentTime)
|
import Data.Time.Clock (getCurrentTime)
|
||||||
|
|
||||||
import Control.Concurrent.STM (TMVar, newEmptyTMVarIO, putTMVar,
|
import Control.Concurrent.STM (newEmptyTMVarIO, putTMVar, readTMVar,
|
||||||
readTMVar, tryReadTMVar, tryTakeTMVar)
|
tryReadTMVar, tryTakeTMVar)
|
||||||
import PostgREST.Auth.JwtCache (JwtCacheState, update)
|
import PostgREST.Auth.JwtCache (update)
|
||||||
import PostgREST.Config (AppConfig (..), readAppConfig,
|
import PostgREST.Config (AppConfig (..), readAppConfig,
|
||||||
toConnectionSettings)
|
toConnectionSettings)
|
||||||
import PostgREST.Config.Database (queryDbSettings, queryPgVersion,
|
import PostgREST.Config.Database (queryDbSettings, queryPgVersion,
|
||||||
@@ -63,45 +63,10 @@ import PostgREST.SchemaCache (SchemaCache (..), querySchemaCache,
|
|||||||
showSummary)
|
showSummary)
|
||||||
import PostgREST.SchemaCache.Identifiers (quoteQi)
|
import PostgREST.SchemaCache.Identifiers (quoteQi)
|
||||||
|
|
||||||
|
import PostgREST.AppState.Types
|
||||||
|
|
||||||
import Protolude
|
import Protolude
|
||||||
|
|
||||||
data AppState = AppState
|
|
||||||
-- | Database connection pool
|
|
||||||
{ statePool :: SQL.Pool
|
|
||||||
-- | Database server version
|
|
||||||
, statePgVersion :: IORef PgVersion
|
|
||||||
-- | Schema cache
|
|
||||||
, stateSchemaCache :: IORef (Maybe SchemaCache)
|
|
||||||
-- | The schema cache status
|
|
||||||
, stateSCacheStatus :: SchemaCacheStatus
|
|
||||||
-- | State of the LISTEN channel
|
|
||||||
, stateIsListenerOn :: IORef Bool
|
|
||||||
-- | starts the connection worker with a debounce
|
|
||||||
, debouncedSCacheLoader :: IO ()
|
|
||||||
-- | Config that can change at runtime
|
|
||||||
, stateConf :: IORef AppConfig
|
|
||||||
-- | Time used for verifying JWT expiration
|
|
||||||
, stateGetTime :: IO UTCTime
|
|
||||||
-- | Used for killing the main thread in case a subthread fails
|
|
||||||
, stateKillApp :: IO ()
|
|
||||||
-- | Keeps track of the next delay for db connection retry
|
|
||||||
, stateNextDelay :: IORef Int
|
|
||||||
-- | Observation handler
|
|
||||||
, stateObserver :: ObservationHandler
|
|
||||||
-- | JWT Cache
|
|
||||||
, stateJwtCache :: JwtCache.JwtCacheState
|
|
||||||
, stateLogger :: Logger.LoggerState
|
|
||||||
, stateMetrics :: Metrics.MetricsState
|
|
||||||
}
|
|
||||||
|
|
||||||
-- | Schema cache status.
|
|
||||||
-- Empty means initial loading on startup, False means pending and True means loaded.
|
|
||||||
-- "Initial" state is needed so that we can wait with application socket listening
|
|
||||||
-- until after initial schema cache querying.
|
|
||||||
newtype SchemaCacheStatus = SchemaCacheStatus
|
|
||||||
{ getSCStatusTMVar :: TMVar Bool
|
|
||||||
}
|
|
||||||
|
|
||||||
init :: AppConfig -> IO () -> IO AppState
|
init :: AppConfig -> IO () -> IO AppState
|
||||||
init conf@AppConfig{configLogLevel, configDbPoolSize} appKiller = do
|
init conf@AppConfig{configLogLevel, configDbPoolSize} appKiller = do
|
||||||
loggerState <- Logger.init
|
loggerState <- Logger.init
|
||||||
@@ -221,39 +186,6 @@ flushPool AppState{..} = do
|
|||||||
SQL.release statePool
|
SQL.release statePool
|
||||||
stateObserver PoolFlushed
|
stateObserver PoolFlushed
|
||||||
|
|
||||||
getPgVersion :: AppState -> IO PgVersion
|
|
||||||
getPgVersion = readIORef . statePgVersion
|
|
||||||
|
|
||||||
putPgVersion :: AppState -> PgVersion -> IO ()
|
|
||||||
putPgVersion = atomicWriteIORef . statePgVersion
|
|
||||||
|
|
||||||
getSchemaCache :: AppState -> IO (Maybe SchemaCache)
|
|
||||||
getSchemaCache = readIORef . stateSchemaCache
|
|
||||||
|
|
||||||
putSchemaCache :: AppState -> Maybe SchemaCache -> IO ()
|
|
||||||
putSchemaCache appState = atomicWriteIORef (stateSchemaCache appState)
|
|
||||||
|
|
||||||
schemaCacheLoader :: AppState -> IO ()
|
|
||||||
schemaCacheLoader = debouncedSCacheLoader
|
|
||||||
|
|
||||||
getNextDelay :: AppState -> IO Int
|
|
||||||
getNextDelay = readIORef . stateNextDelay
|
|
||||||
|
|
||||||
getConfig :: AppState -> IO AppConfig
|
|
||||||
getConfig = readIORef . stateConf
|
|
||||||
|
|
||||||
putConfig :: AppState -> AppConfig -> IO ()
|
|
||||||
putConfig = atomicWriteIORef . stateConf
|
|
||||||
|
|
||||||
getTime :: AppState -> IO UTCTime
|
|
||||||
getTime = stateGetTime
|
|
||||||
|
|
||||||
getJwtCacheState :: AppState -> JwtCacheState
|
|
||||||
getJwtCacheState = stateJwtCache
|
|
||||||
|
|
||||||
killApp :: AppState -> IO ()
|
|
||||||
killApp = stateKillApp
|
|
||||||
|
|
||||||
isConnEstablished :: AppState -> IO Bool
|
isConnEstablished :: AppState -> IO Bool
|
||||||
isConnEstablished appState = do
|
isConnEstablished appState = do
|
||||||
AppConfig{..} <- getConfig appState
|
AppConfig{..} <- getConfig appState
|
||||||
@@ -262,9 +194,6 @@ isConnEstablished appState = do
|
|||||||
else -- otherwise the only way to check the connection is to make a query
|
else -- otherwise the only way to check the connection is to make a query
|
||||||
isRight <$> usePool appState (SQL.sql "SELECT 1")
|
isRight <$> usePool appState (SQL.sql "SELECT 1")
|
||||||
|
|
||||||
putIsListenerOn :: AppState -> Bool -> IO ()
|
|
||||||
putIsListenerOn = atomicWriteIORef . stateIsListenerOn
|
|
||||||
|
|
||||||
isLoaded :: AppState -> IO Bool
|
isLoaded :: AppState -> IO Bool
|
||||||
isLoaded x = do
|
isLoaded x = do
|
||||||
scacheLoaded <- isSchemaCacheLoaded x
|
scacheLoaded <- isSchemaCacheLoaded x
|
||||||
@@ -277,9 +206,6 @@ isPending x = do
|
|||||||
connEstablished <- isConnEstablished x
|
connEstablished <- isConnEstablished x
|
||||||
return $ not scacheLoaded || not connEstablished
|
return $ not scacheLoaded || not connEstablished
|
||||||
|
|
||||||
getObserver :: AppState -> ObservationHandler
|
|
||||||
getObserver = stateObserver
|
|
||||||
|
|
||||||
-- | Try to load the schema cache and retry if it fails.
|
-- | Try to load the schema cache and retry if it fails.
|
||||||
--
|
--
|
||||||
-- This is done by repeatedly: 1) flushing the pool, 2) querying the version and validating that the postgres version is supported by us, and 3) loading the schema cache.
|
-- This is done by repeatedly: 1) flushing the pool, 2) querying the version and validating that the postgres version is supported by us, and 3) loading the schema cache.
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
{-|
|
||||||
|
Module : PostgREST.AppState.Types
|
||||||
|
Description : AppState data type and stateful functions
|
||||||
|
-}
|
||||||
|
module PostgREST.AppState.Types where
|
||||||
|
|
||||||
|
import qualified Hasql.Pool as SQL
|
||||||
|
import qualified PostgREST.Auth.JwtCache as JwtCache
|
||||||
|
import qualified PostgREST.Logger as Logger
|
||||||
|
import qualified PostgREST.Metrics as Metrics
|
||||||
|
import PostgREST.Observation
|
||||||
|
|
||||||
|
import Data.IORef (IORef, atomicWriteIORef, readIORef)
|
||||||
|
import Data.Time.Clock (UTCTime)
|
||||||
|
|
||||||
|
import Control.Concurrent.STM (TMVar)
|
||||||
|
import PostgREST.Auth.JwtCache (JwtCacheState)
|
||||||
|
import PostgREST.Config (AppConfig (..))
|
||||||
|
import PostgREST.Config.PgVersion (PgVersion (..))
|
||||||
|
import PostgREST.SchemaCache (SchemaCache (..))
|
||||||
|
|
||||||
|
import Protolude
|
||||||
|
|
||||||
|
data AppState = AppState
|
||||||
|
-- | Database connection pool
|
||||||
|
{ statePool :: SQL.Pool
|
||||||
|
-- | Database server version
|
||||||
|
, statePgVersion :: IORef PgVersion
|
||||||
|
-- | Schema cache
|
||||||
|
, stateSchemaCache :: IORef (Maybe SchemaCache)
|
||||||
|
-- | The schema cache status
|
||||||
|
, stateSCacheStatus :: SchemaCacheStatus
|
||||||
|
-- | State of the LISTEN channel
|
||||||
|
, stateIsListenerOn :: IORef Bool
|
||||||
|
-- | starts the connection worker with a debounce
|
||||||
|
, debouncedSCacheLoader :: IO ()
|
||||||
|
-- | Config that can change at runtime
|
||||||
|
, stateConf :: IORef AppConfig
|
||||||
|
-- | Time used for verifying JWT expiration
|
||||||
|
, stateGetTime :: IO UTCTime
|
||||||
|
-- | Used for killing the main thread in case a subthread fails
|
||||||
|
, stateKillApp :: IO ()
|
||||||
|
-- | Keeps track of the next delay for db connection retry
|
||||||
|
, stateNextDelay :: IORef Int
|
||||||
|
-- | Observation handler
|
||||||
|
, stateObserver :: ObservationHandler
|
||||||
|
-- | JWT Cache
|
||||||
|
, stateJwtCache :: JwtCache.JwtCacheState
|
||||||
|
, stateLogger :: Logger.LoggerState
|
||||||
|
, stateMetrics :: Metrics.MetricsState
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Schema cache status.
|
||||||
|
-- Empty means initial loading on startup, False means pending and True means loaded.
|
||||||
|
-- "Initial" state is needed so that we can wait with application socket listening
|
||||||
|
-- until after initial schema cache querying.
|
||||||
|
newtype SchemaCacheStatus = SchemaCacheStatus
|
||||||
|
{ getSCStatusTMVar :: TMVar Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
getPgVersion :: AppState -> IO PgVersion
|
||||||
|
getPgVersion = readIORef . statePgVersion
|
||||||
|
|
||||||
|
putPgVersion :: AppState -> PgVersion -> IO ()
|
||||||
|
putPgVersion = atomicWriteIORef . statePgVersion
|
||||||
|
|
||||||
|
getSchemaCache :: AppState -> IO (Maybe SchemaCache)
|
||||||
|
getSchemaCache = readIORef . stateSchemaCache
|
||||||
|
|
||||||
|
putSchemaCache :: AppState -> Maybe SchemaCache -> IO ()
|
||||||
|
putSchemaCache appState = atomicWriteIORef (stateSchemaCache appState)
|
||||||
|
|
||||||
|
schemaCacheLoader :: AppState -> IO ()
|
||||||
|
schemaCacheLoader = debouncedSCacheLoader
|
||||||
|
|
||||||
|
getNextDelay :: AppState -> IO Int
|
||||||
|
getNextDelay = readIORef . stateNextDelay
|
||||||
|
|
||||||
|
getConfig :: AppState -> IO AppConfig
|
||||||
|
getConfig = readIORef . stateConf
|
||||||
|
|
||||||
|
putConfig :: AppState -> AppConfig -> IO ()
|
||||||
|
putConfig = atomicWriteIORef . stateConf
|
||||||
|
|
||||||
|
getTime :: AppState -> IO UTCTime
|
||||||
|
getTime = stateGetTime
|
||||||
|
|
||||||
|
getJwtCacheState :: AppState -> JwtCacheState
|
||||||
|
getJwtCacheState = stateJwtCache
|
||||||
|
|
||||||
|
killApp :: AppState -> IO ()
|
||||||
|
killApp = stateKillApp
|
||||||
|
|
||||||
|
putIsListenerOn :: AppState -> Bool -> IO ()
|
||||||
|
putIsListenerOn = atomicWriteIORef . stateIsListenerOn
|
||||||
|
|
||||||
|
getObserver :: AppState -> ObservationHandler
|
||||||
|
getObserver = stateObserver
|
||||||
Reference in New Issue
Block a user