refactor: remove pgVersion from DbStructure

Also use a dedicated MVar for listener
This commit is contained in:
steve-chavez
2021-04-30 10:06:07 -05:00
committed by Steve Chavez
parent 0f6a13191c
commit 082c91c855
6 changed files with 66 additions and 53 deletions
+16 -12
View File
@@ -60,6 +60,7 @@ import PostgREST.DbStructure (DbStructure (..),
import PostgREST.DbStructure.Identifiers (FieldName, import PostgREST.DbStructure.Identifiers (FieldName,
QualifiedIdentifier (..), QualifiedIdentifier (..),
Schema) Schema)
import PostgREST.DbStructure.PgVersion (PgVersion (..))
import PostgREST.DbStructure.Proc (ProcDescription (..), import PostgREST.DbStructure.Proc (ProcDescription (..),
ProcVolatility (..)) ProcVolatility (..))
import PostgREST.DbStructure.Table (Table (..)) import PostgREST.DbStructure.Table (Table (..))
@@ -89,6 +90,7 @@ data RequestContext = RequestContext
{ ctxConfig :: AppConfig { ctxConfig :: AppConfig
, ctxDbStructure :: DbStructure , ctxDbStructure :: DbStructure
, ctxApiRequest :: ApiRequest , ctxApiRequest :: ApiRequest
, ctxPgVersion :: PgVersion
} }
type Handler = ExceptT Error type Handler = ExceptT Error
@@ -138,11 +140,12 @@ postgrest logLev appState connWorker =
time <- AppState.getTime appState time <- AppState.getTime appState
conf <- AppState.getConfig appState conf <- AppState.getConfig appState
maybeDbStructure <- AppState.getDbStructure appState maybeDbStructure <- AppState.getDbStructure appState
pgVer <- AppState.getPgVersion appState
let let
eitherResponse :: IO (Either Error Wai.Response) eitherResponse :: IO (Either Error Wai.Response)
eitherResponse = eitherResponse =
runExceptT $ postgrestResponse conf maybeDbStructure (AppState.getPool appState) time req runExceptT $ postgrestResponse conf maybeDbStructure pgVer (AppState.getPool appState) time req
response <- either Error.errorResponseFor identity <$> eitherResponse response <- either Error.errorResponseFor identity <$> eitherResponse
@@ -156,11 +159,12 @@ postgrest logLev appState connWorker =
postgrestResponse postgrestResponse
:: AppConfig :: AppConfig
-> Maybe DbStructure -> Maybe DbStructure
-> PgVersion
-> SQL.Pool -> SQL.Pool
-> UTCTime -> UTCTime
-> Wai.Request -> Wai.Request
-> Handler IO Wai.Response -> Handler IO Wai.Response
postgrestResponse conf maybeDbStructure pool time req = do postgrestResponse conf maybeDbStructure pgVer pool time req = do
body <- lift $ Wai.strictRequestBody req body <- lift $ Wai.strictRequestBody req
dbStructure <- dbStructure <-
@@ -179,7 +183,7 @@ postgrestResponse conf maybeDbStructure pool time req = do
let let
handleReq apiReq = handleReq apiReq =
handleRequest $ RequestContext conf dbStructure apiReq handleRequest $ RequestContext conf dbStructure apiReq pgVer
runDbHandler pool (txMode apiRequest) jwtClaims . runDbHandler pool (txMode apiRequest) jwtClaims .
Middleware.optionalRollback conf apiRequest $ Middleware.optionalRollback conf apiRequest $
@@ -197,7 +201,7 @@ runDbHandler pool mode jwtClaims handler = do
liftEither resp liftEither resp
handleRequest :: RequestContext -> DbHandler Wai.Response handleRequest :: RequestContext -> DbHandler Wai.Response
handleRequest context@(RequestContext _ _ ApiRequest{..}) = handleRequest context@(RequestContext _ _ ApiRequest{..} _) =
case (iAction, iTarget) of case (iAction, iTarget) of
(ActionRead headersOnly, TargetIdent identifier) -> (ActionRead headersOnly, TargetIdent identifier) ->
handleRead headersOnly identifier context handleRead headersOnly identifier context
@@ -242,7 +246,7 @@ handleRead headersOnly identifier context@RequestContext{..} = do
(shouldCount iPreferCount) (shouldCount iPreferCount)
(iAcceptContentType == CTTextCSV) (iAcceptContentType == CTTextCSV)
bField bField
(pgVersion ctxDbStructure) ctxPgVersion
configDbPreparedStatements configDbPreparedStatements
total <- readTotal ctxConfig ctxApiRequest tableTotal countQuery total <- readTotal ctxConfig ctxApiRequest tableTotal countQuery
@@ -316,7 +320,7 @@ handleCreate identifier@QualifiedIdentifier{..} context@RequestContext{..} = do
response HTTP.status201 headers mempty response HTTP.status201 headers mempty
handleUpdate :: QualifiedIdentifier -> RequestContext -> DbHandler Wai.Response handleUpdate :: QualifiedIdentifier -> RequestContext -> DbHandler Wai.Response
handleUpdate identifier context@(RequestContext _ _ ApiRequest{..}) = do handleUpdate identifier context@(RequestContext _ _ ApiRequest{..} _) = do
WriteQueryResult{..} <- writeQuery identifier False mempty context WriteQueryResult{..} <- writeQuery identifier False mempty context
let let
@@ -338,7 +342,7 @@ handleUpdate identifier context@(RequestContext _ _ ApiRequest{..}) = do
response status [contentRangeHeader] mempty response status [contentRangeHeader] mempty
handleSingleUpsert :: QualifiedIdentifier -> RequestContext-> DbHandler Wai.Response handleSingleUpsert :: QualifiedIdentifier -> RequestContext-> DbHandler Wai.Response
handleSingleUpsert identifier context@(RequestContext _ _ ApiRequest{..}) = do handleSingleUpsert identifier context@(RequestContext _ _ ApiRequest{..} _) = do
when (iTopLevelRange /= RangeQuery.allRange) $ when (iTopLevelRange /= RangeQuery.allRange) $
throwError Error.PutRangeNotAllowedError throwError Error.PutRangeNotAllowedError
@@ -362,7 +366,7 @@ handleSingleUpsert identifier context@(RequestContext _ _ ApiRequest{..}) = do
response HTTP.status204 (contentTypeHeaders context) mempty response HTTP.status204 (contentTypeHeaders context) mempty
handleDelete :: QualifiedIdentifier -> RequestContext -> DbHandler Wai.Response handleDelete :: QualifiedIdentifier -> RequestContext -> DbHandler Wai.Response
handleDelete identifier context@(RequestContext _ _ ApiRequest{..}) = do handleDelete identifier context@(RequestContext _ _ ApiRequest{..} _) = do
WriteQueryResult{..} <- writeQuery identifier False mempty context WriteQueryResult{..} <- writeQuery identifier False mempty context
let let
@@ -439,7 +443,7 @@ handleInvoke invMethod proc context@RequestContext{..} = do
(iAcceptContentType == CTTextCSV) (iAcceptContentType == CTTextCSV)
(iPreferParameters == Just MultipleObjects) (iPreferParameters == Just MultipleObjects)
bField bField
(pgVersion ctxDbStructure) ctxPgVersion
(configDbPreparedStatements ctxConfig) (configDbPreparedStatements ctxConfig)
response <- liftEither $ gucResponse <$> gucStatus <*> gucHeaders response <- liftEither $ gucResponse <$> gucStatus <*> gucHeaders
@@ -454,7 +458,7 @@ handleInvoke invMethod proc context@RequestContext{..} = do
(if invMethod == InvHead then mempty else toS body) (if invMethod == InvHead then mempty else toS body)
handleOpenApi :: Bool -> Schema -> RequestContext -> DbHandler Wai.Response handleOpenApi :: Bool -> Schema -> RequestContext -> DbHandler Wai.Response
handleOpenApi headersOnly tSchema (RequestContext conf@AppConfig{..} dbStructure apiRequest) = do handleOpenApi headersOnly tSchema (RequestContext conf@AppConfig{..} dbStructure apiRequest _) = do
body <- body <-
lift $ lift $
OpenAPI.encode conf dbStructure OpenAPI.encode conf dbStructure
@@ -516,7 +520,7 @@ writeQuery identifier@QualifiedIdentifier{..} isInsert pkCols context@RequestCon
(iAcceptContentType ctxApiRequest == CTTextCSV) (iAcceptContentType ctxApiRequest == CTTextCSV)
(iPreferRepresentation ctxApiRequest) (iPreferRepresentation ctxApiRequest)
pkCols pkCols
(pgVersion ctxDbStructure) ctxPgVersion
(configDbPreparedStatements ctxConfig) (configDbPreparedStatements ctxConfig)
liftEither $ WriteQueryResult queryTotal fields body <$> gucStatus <*> gucHeaders liftEither $ WriteQueryResult queryTotal fields body <$> gucStatus <*> gucHeaders
@@ -554,7 +558,7 @@ returnsScalar (TargetProc proc _) = Proc.procReturnsScalar proc
returnsScalar _ = False returnsScalar _ = False
readRequest :: Monad m => QualifiedIdentifier -> RequestContext -> Handler m ReadRequest readRequest :: Monad m => QualifiedIdentifier -> RequestContext -> Handler m ReadRequest
readRequest QualifiedIdentifier{..} (RequestContext AppConfig{..} dbStructure apiRequest) = readRequest QualifiedIdentifier{..} (RequestContext AppConfig{..} dbStructure apiRequest _) =
liftEither $ liftEither $
ReqBuilder.readRequest qiSchema qiName configDbMaxRows ReqBuilder.readRequest qiSchema qiName configDbMaxRows
(dbRelationships dbStructure) (dbRelationships dbStructure)
+23 -10
View File
@@ -16,6 +16,8 @@ module PostgREST.AppState
, putIsWorkerOn , putIsWorkerOn
, putPgVersion , putPgVersion
, releasePool , releasePool
, signalListener
, waitListener
) where ) where
import qualified Hasql.Pool as P import qualified Hasql.Pool as P
@@ -28,7 +30,8 @@ import Data.Time.Clock (UTCTime, getCurrentTime)
import PostgREST.Config (AppConfig (..)) import PostgREST.Config (AppConfig (..))
import PostgREST.DbStructure (DbStructure) import PostgREST.DbStructure (DbStructure)
import PostgREST.DbStructure.PgVersion (PgVersion (..)) import PostgREST.DbStructure.PgVersion (PgVersion (..),
minimumPgVersion)
import Protolude hiding (toS) import Protolude hiding (toS)
import Protolude.Conv (toS) import Protolude.Conv (toS)
@@ -36,13 +39,13 @@ import Protolude.Conv (toS)
data AppState = AppState data AppState = AppState
{ statePool :: P.Pool -- | Connection pool, either a 'Connection' or a 'ConnectionError' { statePool :: P.Pool -- | Connection pool, either a 'Connection' or a 'ConnectionError'
-- | Used to sync the listener(NOTIFY reload) with the connectionWorker. No , statePgVersion :: IORef PgVersion
-- connection for the listener at first. Only used if dbChannelEnabled=true.
, statePgVersion :: MVar PgVersion
-- | No schema cache at the start. Will be filled in by the connectionWorker -- | No schema cache at the start. Will be filled in by the connectionWorker
, stateDbStructure :: IORef (Maybe DbStructure) , stateDbStructure :: IORef (Maybe DbStructure)
-- | Helper ref to make sure just one connectionWorker can run at a time -- | Helper ref to make sure just one connectionWorker can run at a time
, stateIsWorkerOn :: IORef Bool , stateIsWorkerOn :: IORef Bool
-- | Binary semaphore used to sync the listener(NOTIFY reload) with the connectionWorker.
, stateListener :: MVar ()
-- | Config that can change at runtime -- | Config that can change at runtime
, stateConf :: IORef AppConfig , stateConf :: IORef AppConfig
, stateGetTime :: IO UTCTime , stateGetTime :: IO UTCTime
@@ -57,9 +60,11 @@ init conf = do
initWithPool :: P.Pool -> AppConfig -> IO AppState initWithPool :: P.Pool -> AppConfig -> IO AppState
initWithPool newPool conf = initWithPool newPool conf =
AppState newPool AppState newPool
<$> newEmptyMVar -- assume we're in a supported version when starting, this will be corrected on a later step
<$> newIORef minimumPgVersion
<*> newIORef Nothing <*> newIORef Nothing
<*> newIORef False <*> newIORef False
<*> newEmptyMVar
<*> newIORef conf <*> newIORef conf
<*> mkAutoUpdate defaultUpdateSettings { updateAction = getCurrentTime } <*> mkAutoUpdate defaultUpdateSettings { updateAction = getCurrentTime }
<*> myThreadId <*> myThreadId
@@ -74,14 +79,11 @@ getPool = statePool
releasePool :: AppState -> IO () releasePool :: AppState -> IO ()
releasePool AppState{..} = P.release statePool >> throwTo stateMainThreadId UserInterrupt releasePool AppState{..} = P.release statePool >> throwTo stateMainThreadId UserInterrupt
-- | As this IO action uses `takeMVar` internally, it will only return once
-- `statePgVersion` has been set using `putPgVersion`. This is currently used
-- to syncronize workers.
getPgVersion :: AppState -> IO PgVersion getPgVersion :: AppState -> IO PgVersion
getPgVersion = takeMVar . statePgVersion getPgVersion = readIORef . statePgVersion
putPgVersion :: AppState -> PgVersion -> IO () putPgVersion :: AppState -> PgVersion -> IO ()
putPgVersion appState pgVer = void $ tryPutMVar (statePgVersion appState) pgVer putPgVersion = atomicWriteIORef . statePgVersion
getDbStructure :: AppState -> IO (Maybe DbStructure) getDbStructure :: AppState -> IO (Maybe DbStructure)
getDbStructure = readIORef . stateDbStructure getDbStructure = readIORef . stateDbStructure
@@ -107,3 +109,14 @@ getTime = stateGetTime
getMainThreadId :: AppState -> ThreadId getMainThreadId :: AppState -> ThreadId
getMainThreadId = stateMainThreadId getMainThreadId = stateMainThreadId
-- | As this IO action uses `takeMVar` internally, it will only return once
-- `stateListener` has been set using `signalListener`. This is currently used
-- to syncronize workers.
waitListener :: AppState -> IO ()
waitListener = takeMVar . stateListener
-- tryPutMVar doesn't lock the thread. It should always succeed since
-- the connectionWorker is the only mvar producer.
signalListener :: AppState -> IO ()
signalListener appState = void $ tryPutMVar (stateListener appState) ()
+2 -4
View File
@@ -20,7 +20,7 @@ import Text.Heredoc (str)
import PostgREST.AppState (AppState) import PostgREST.AppState (AppState)
import PostgREST.Config (AppConfig (..)) import PostgREST.Config (AppConfig (..))
import PostgREST.DbStructure (getDbStructure, getPgVersion) import PostgREST.DbStructure (getDbStructure)
import PostgREST.Version (prettyVersion) import PostgREST.Version (prettyVersion)
import PostgREST.Workers (reReadConfig) import PostgREST.Workers (reReadConfig)
@@ -54,13 +54,11 @@ dumpSchema :: AppState -> IO LBS.ByteString
dumpSchema appState = do dumpSchema appState = do
AppConfig{..} <- AppState.getConfig appState AppConfig{..} <- AppState.getConfig appState
result <- result <-
P.use (AppState.getPool appState) $ do P.use (AppState.getPool appState) $
pgVersion <- getPgVersion
HT.transaction HT.ReadCommitted HT.Read $ HT.transaction HT.ReadCommitted HT.Read $
getDbStructure getDbStructure
(toList configDbSchemas) (toList configDbSchemas)
configDbExtraSearchPath configDbExtraSearchPath
pgVersion
configDbPreparedStatements configDbPreparedStatements
P.release $ AppState.getPool appState P.release $ AppState.getPool appState
case result of case result of
+2 -5
View File
@@ -67,7 +67,6 @@ data DbStructure = DbStructure
, dbRelationships :: [Relationship] , dbRelationships :: [Relationship]
, dbPrimaryKeys :: [PrimaryKey] , dbPrimaryKeys :: [PrimaryKey]
, dbProcs :: ProcsMap , dbProcs :: ProcsMap
, pgVersion :: PgVersion
} }
deriving (Generic, JSON.ToJSON) deriving (Generic, JSON.ToJSON)
@@ -86,8 +85,8 @@ type ViewColumn = Column
-- | A SQL query that can be executed independently -- | A SQL query that can be executed independently
type SqlQuery = ByteString type SqlQuery = ByteString
getDbStructure :: [Schema] -> [Schema] -> PgVersion -> Bool -> HT.Transaction DbStructure getDbStructure :: [Schema] -> [Schema] -> Bool -> HT.Transaction DbStructure
getDbStructure schemas extraSearchPath pgVer prepared = do getDbStructure schemas extraSearchPath prepared = do
HT.sql "set local schema ''" -- This voids the search path. The following queries need this for getting the fully qualified name(schema.name) of every db object HT.sql "set local schema ''" -- This voids the search path. The following queries need this for getting the fully qualified name(schema.name) of every db object
tabs <- HT.statement mempty $ allTables prepared tabs <- HT.statement mempty $ allTables prepared
cols <- HT.statement schemas $ allColumns tabs prepared cols <- HT.statement schemas $ allColumns tabs prepared
@@ -105,7 +104,6 @@ getDbStructure schemas extraSearchPath pgVer prepared = do
, dbRelationships = rels , dbRelationships = rels
, dbPrimaryKeys = keys' , dbPrimaryKeys = keys'
, dbProcs = procs , dbProcs = procs
, pgVersion = pgVer
} }
-- | Remove db objects that belong to an internal schema(not exposed through the API) from the DbStructure. -- | Remove db objects that belong to an internal schema(not exposed through the API) from the DbStructure.
@@ -119,7 +117,6 @@ removeInternal schemas dbStruct =
not (hasInternalJunction x)) $ dbRelationships dbStruct not (hasInternalJunction x)) $ dbRelationships dbStruct
, dbPrimaryKeys = filter (\x -> tableSchema (pkTable x) `elem` schemas) $ dbPrimaryKeys dbStruct , dbPrimaryKeys = filter (\x -> tableSchema (pkTable x) `elem` schemas) $ dbPrimaryKeys dbStruct
, dbProcs = dbProcs dbStruct -- procs are only obtained from the exposed schemas, no need to filter them. , dbProcs = dbProcs dbStruct -- procs are only obtained from the exposed schemas, no need to filter them.
, pgVersion = pgVersion dbStruct
} }
where where
hasInternalJunction rel = case relCardinality rel of hasInternalJunction rel = case relCardinality rel of
+20 -18
View File
@@ -77,16 +77,15 @@ connectionWorker appState = do
-- Unreachable because connectionStatus will keep trying to connect -- Unreachable because connectionStatus will keep trying to connect
return () return ()
Connected actualPgVersion -> do Connected actualPgVersion -> do
when configDbChannelEnabled $
-- tryPutMVar doesn't lock the thread. It should always succeed since
-- the worker is the only mvar producer.
AppState.putPgVersion appState actualPgVersion
-- Procede with initialization -- Procede with initialization
AppState.putPgVersion appState actualPgVersion
when configDbChannelEnabled $
AppState.signalListener appState
putStrLn ("Connection successful" :: Text) putStrLn ("Connection successful" :: Text)
-- this could be fail because the connection drops, but the -- this could be fail because the connection drops, but the
-- loadSchemaCache will pick the error and retry again -- loadSchemaCache will pick the error and retry again
when configDbConfig $ reReadConfig False appState when configDbConfig $ reReadConfig False appState
scStatus <- loadSchemaCache appState actualPgVersion scStatus <- loadSchemaCache appState
case scStatus of case scStatus of
SCLoaded -> SCLoaded ->
-- do nothing and proceed if the load was successful -- do nothing and proceed if the load was successful
@@ -148,12 +147,12 @@ connectionStatus pool =
return itShould return itShould
-- | Load the DbStructure by using a connection from the pool. -- | Load the DbStructure by using a connection from the pool.
loadSchemaCache :: AppState -> PgVersion -> IO SCacheStatus loadSchemaCache :: AppState -> IO SCacheStatus
loadSchemaCache appState actualPgVersion = do loadSchemaCache appState = do
AppConfig{..} <- AppState.getConfig appState AppConfig{..} <- AppState.getConfig appState
result <- result <-
P.use (AppState.getPool appState) . HT.transaction HT.ReadCommitted HT.Read $ P.use (AppState.getPool appState) . HT.transaction HT.ReadCommitted HT.Read $
getDbStructure (toList configDbSchemas) configDbExtraSearchPath actualPgVersion configDbPreparedStatements getDbStructure (toList configDbSchemas) configDbExtraSearchPath configDbPreparedStatements
case result of case result of
Left e -> do Left e -> do
let let
@@ -185,9 +184,12 @@ listener appState = do
AppConfig{..} <- AppState.getConfig appState AppConfig{..} <- AppState.getConfig appState
let dbChannel = toS configDbChannel let dbChannel = toS configDbChannel
-- AppState.getPgVersion makes the thread wait until the pgVersion has been -- The listener has to wait for a signal from the connectionWorker.
-- set by the connectionWorker -- This is because when the connection to the db is lost, the listener also
actualPgVersion <- AppState.getPgVersion appState -- tries to recover the connection, but not with the same pace as the connectionWorker.
-- Not waiting makes stdout quickly fill with connection retries messages from the listener.
AppState.waitListener appState
-- forkFinally allows to detect if the thread dies -- forkFinally allows to detect if the thread dies
void . flip forkFinally (handleFinally dbChannel) $ do void . flip forkFinally (handleFinally dbChannel) $ do
dbOrError <- C.acquire $ toS configDbUri dbOrError <- C.acquire $ toS configDbUri
@@ -195,7 +197,7 @@ listener appState = do
Right db -> do Right db -> do
putStrLn $ "Listening for notifications on the " <> dbChannel <> " channel" putStrLn $ "Listening for notifications on the " <> dbChannel <> " channel"
N.listen db $ N.toPgIdentifier dbChannel N.listen db $ N.toPgIdentifier dbChannel
N.waitForNotifications (handleNotification actualPgVersion) db N.waitForNotifications handleNotification db
_ -> _ ->
die $ "Could not listen for notifications on the " <> dbChannel <> " channel" die $ "Could not listen for notifications on the " <> dbChannel <> " channel"
where where
@@ -207,17 +209,17 @@ listener appState = do
-- retry the listener -- retry the listener
listener appState listener appState
handleNotification actualPgVersion _ msg handleNotification _ msg
| BS.null msg = scLoader actualPgVersion -- reload the schema cache | BS.null msg = scLoader -- reload the schema cache
| msg == "reload schema" = scLoader actualPgVersion -- reload the schema cache | msg == "reload schema" = scLoader -- reload the schema cache
| msg == "reload config" = reReadConfig False appState -- reload the config | msg == "reload config" = reReadConfig False appState -- reload the config
| otherwise = pure () -- Do nothing if anything else than an empty message is sent | otherwise = pure () -- Do nothing if anything else than an empty message is sent
scLoader actualPgVersion = scLoader =
-- It's not necessary to check the loadSchemaCache success -- It's not necessary to check the loadSchemaCache success
-- here. If the connection drops, the thread will die and -- here. If the connection drops, the thread will die and
-- proceed to recover below. -- proceed to recover.
void $ loadSchemaCache appState actualPgVersion void $ loadSchemaCache appState
-- | Re-reads the config plus config options from the db -- | Re-reads the config plus config options from the db
reReadConfig :: Bool -> AppState -> IO () reReadConfig :: Bool -> AppState -> IO ()
+3 -4
View File
@@ -63,12 +63,12 @@ main = do
loadDbStructure pool loadDbStructure pool
(configDbSchemas $ testCfg testDbConn) (configDbSchemas $ testCfg testDbConn)
(configDbExtraSearchPath $ testCfg testDbConn) (configDbExtraSearchPath $ testCfg testDbConn)
actualPgVersion
let let
-- For tests that run with the same refDbStructure -- For tests that run with the same refDbStructure
app cfg = do app cfg = do
appState <- AppState.initWithPool pool $ cfg testDbConn appState <- AppState.initWithPool pool $ cfg testDbConn
AppState.putPgVersion appState actualPgVersion
AppState.putDbStructure appState baseDbStructure AppState.putDbStructure appState baseDbStructure
return ((), postgrest LogCrit appState $ pure ()) return ((), postgrest LogCrit appState $ pure ())
@@ -78,7 +78,6 @@ main = do
loadDbStructure pool loadDbStructure pool
(configDbSchemas $ cfg testDbConn) (configDbSchemas $ cfg testDbConn)
(configDbExtraSearchPath $ cfg testDbConn) (configDbExtraSearchPath $ cfg testDbConn)
actualPgVersion
appState <- AppState.initWithPool pool $ cfg testDbConn appState <- AppState.initWithPool pool $ cfg testDbConn
AppState.putDbStructure appState customDbStructure AppState.putDbStructure appState customDbStructure
return ((), postgrest LogCrit appState $ pure ()) return ((), postgrest LogCrit appState $ pure ())
@@ -204,5 +203,5 @@ main = do
describe "Feature.RollbackForcedSpec" Feature.RollbackSpec.forced describe "Feature.RollbackForcedSpec" Feature.RollbackSpec.forced
where where
loadDbStructure pool schemas extraSearchPath ver = loadDbStructure pool schemas extraSearchPath =
either (panic.show) id <$> P.use pool (HT.transaction HT.ReadCommitted HT.Read $ getDbStructure (toList schemas) extraSearchPath ver True) either (panic.show) id <$> P.use pool (HT.transaction HT.ReadCommitted HT.Read $ getDbStructure (toList schemas) extraSearchPath True)