@@ -27,6 +27,7 @@ module PostgREST.AppState
27
27
, reReadConfig
28
28
, connectionWorker
29
29
, runListener
30
+ , getObserver
30
31
) where
31
32
32
33
import qualified Data.Aeson as JSON
@@ -43,6 +44,7 @@ import qualified Hasql.Transaction.Sessions as SQL
43
44
import qualified Network.HTTP.Types.Status as HTTP
44
45
import qualified Network.Socket as NS
45
46
import qualified PostgREST.Error as Error
47
+ import qualified PostgREST.Logger as Logger
46
48
import PostgREST.Observation
47
49
import PostgREST.Version (prettyVersion )
48
50
import System.TimeIt (timeItT )
@@ -57,7 +59,6 @@ import Data.IORef (IORef, atomicWriteIORef, newIORef,
57
59
import Data.Time.Clock (UTCTime , getCurrentTime )
58
60
59
61
import PostgREST.Config (AppConfig (.. ),
60
- LogLevel (.. ),
61
62
addFallbackAppName ,
62
63
readAppConfig )
63
64
import PostgREST.Config.Database (queryDbSettings ,
@@ -109,19 +110,26 @@ data AppState = AppState
109
110
, stateSocketREST :: NS. Socket
110
111
-- | Network socket for the admin UI
111
112
, stateSocketAdmin :: Maybe NS. Socket
113
+ -- | Logger state
114
+ , stateLogger :: Logger. LoggerState
115
+ -- | Observation handler
116
+ , stateObserver :: ObservationHandler
112
117
}
113
118
114
119
type AppSockets = (NS. Socket , Maybe NS. Socket )
115
120
116
121
init :: AppConfig -> IO AppState
117
- init conf = do
122
+ init conf@ AppConfig {configLogLevel} = do
123
+ loggerState <- Logger. init
124
+ let observer = Logger. observationLogger loggerState configLogLevel
118
125
pool <- initPool conf
119
126
(sock, adminSock) <- initSockets conf
120
- state' <- initWithPool (sock, adminSock) pool conf
121
- pure state' { stateSocketREST = sock, stateSocketAdmin = adminSock }
127
+ state' <- initWithPool (sock, adminSock) pool conf loggerState observer
128
+ pure state' { stateSocketREST = sock, stateSocketAdmin = adminSock}
129
+
130
+ initWithPool :: AppSockets -> SQL. Pool -> AppConfig -> Logger. LoggerState -> ObservationHandler -> IO AppState
131
+ initWithPool (sock, adminSock) pool conf loggerState observer = do
122
132
123
- initWithPool :: AppSockets -> SQL. Pool -> AppConfig -> IO AppState
124
- initWithPool (sock, adminSock) pool conf = do
125
133
appState <- AppState pool
126
134
<$> newIORef minimumPgVersion -- assume we're in a supported version when starting, this will be corrected on a later step
127
135
<*> newIORef Nothing
@@ -136,6 +144,8 @@ initWithPool (sock, adminSock) pool conf = do
136
144
<*> C. newCache Nothing
137
145
<*> pure sock
138
146
<*> pure adminSock
147
+ <*> pure loggerState
148
+ <*> pure observer
139
149
140
150
debWorker <-
141
151
let decisecond = 100000 in
@@ -193,17 +203,16 @@ initPool AppConfig{..} =
193
203
(toUtf8 $ addFallbackAppName prettyVersion configDbUri)
194
204
195
205
-- | Run an action with a database connection.
196
- usePool :: AppState -> AppConfig -> SQL. Session a -> IO (Either SQL. UsageError a )
197
- usePool AppState {.. } AppConfig {configLogLevel, configObserver = observer} sess = do
206
+ usePool :: AppState -> SQL. Session a -> IO (Either SQL. UsageError a )
207
+ usePool AppState {stateObserver = observer, .. } sess = do
198
208
res <- SQL. use statePool sess
199
209
200
- when (configLogLevel > LogCrit ) $ do
201
- whenLeft res (\ case
202
- SQL. AcquisitionTimeoutUsageError -> observer $ PoolAcqTimeoutObs SQL. AcquisitionTimeoutUsageError
203
- error
204
- -- TODO We're using the 500 HTTP status for getting all internal db errors but there's no response here. We need a new intermediate type to not rely on the HTTP status.
205
- | Error. status (Error. PgError False error ) >= HTTP. status500 -> observer $ QueryErrorCodeHighObs error
206
- | otherwise -> pure () )
210
+ whenLeft res (\ case
211
+ SQL. AcquisitionTimeoutUsageError -> observer $ PoolAcqTimeoutObs SQL. AcquisitionTimeoutUsageError
212
+ error
213
+ -- TODO We're using the 500 HTTP status for getting all internal db errors but there's no response here. We need a new intermediate type to not rely on the HTTP status.
214
+ | Error. status (Error. PgError False error ) >= HTTP. status500 -> observer $ QueryErrorCodeHighObs error
215
+ | otherwise -> pure () )
207
216
208
217
return res
209
218
@@ -281,19 +290,22 @@ getSchemaCacheLoaded = readIORef . stateSchemaCacheLoaded
281
290
putSchemaCacheLoaded :: AppState -> Bool -> IO ()
282
291
putSchemaCacheLoaded = atomicWriteIORef . stateSchemaCacheLoaded
283
292
293
+ getObserver :: AppState -> ObservationHandler
294
+ getObserver = stateObserver
295
+
284
296
-- | Schema cache status
285
297
data SCacheStatus
286
298
= SCLoaded
287
299
| SCOnRetry
288
300
| SCFatalFail
289
301
290
302
-- | Load the SchemaCache by using a connection from the pool.
291
- loadSchemaCache :: AppState -> AppConfig -> IO SCacheStatus
292
- loadSchemaCache appState AppConfig {configObserver = observer} = do
303
+ loadSchemaCache :: AppState -> IO SCacheStatus
304
+ loadSchemaCache appState@ AppState {stateObserver = observer} = do
293
305
conf@ AppConfig {.. } <- getConfig appState
294
306
(resultTime, result) <-
295
307
let transaction = if configDbPreparedStatements then SQL. transaction else SQL. unpreparedTransaction in
296
- timeItT $ usePool appState conf (transaction SQL. ReadCommitted SQL. Read $ querySchemaCache conf)
308
+ timeItT $ usePool appState (transaction SQL. ReadCommitted SQL. Read $ querySchemaCache conf)
297
309
case result of
298
310
Left e -> do
299
311
case checkIsFatal e of
@@ -333,12 +345,12 @@ data ConnectionStatus
333
345
-- program.
334
346
-- 3. Obtains the sCache. If this fails, it goes back to 1.
335
347
internalConnectionWorker :: AppState -> IO ()
336
- internalConnectionWorker appState = work
348
+ internalConnectionWorker appState@ AppState {stateObserver = observer} = work
337
349
where
338
350
work = do
339
- config @ AppConfig {configObserver = observer, .. } <- getConfig appState
351
+ AppConfig {.. } <- getConfig appState
340
352
observer DBConnectAttemptObs
341
- connected <- establishConnection appState config
353
+ connected <- establishConnection appState
342
354
case connected of
343
355
FatalConnectionError reason ->
344
356
-- Fatal error when connecting
@@ -356,7 +368,7 @@ internalConnectionWorker appState = work
356
368
-- this could be fail because the connection drops, but the loadSchemaCache will pick the error and retry again
357
369
-- We cannot retry after it fails immediately, because db-pre-config could have user errors. We just log the error and continue.
358
370
when configDbConfig $ reReadConfig False appState
359
- scStatus <- loadSchemaCache appState config
371
+ scStatus <- loadSchemaCache appState
360
372
case scStatus of
361
373
SCLoaded ->
362
374
-- do nothing and proceed if the load was successful
@@ -378,8 +390,8 @@ internalConnectionWorker appState = work
378
390
--
379
391
-- The connection tries are capped, but if the connection times out no error is
380
392
-- thrown, just 'False' is returned.
381
- establishConnection :: AppState -> AppConfig -> IO ConnectionStatus
382
- establishConnection appState config @ AppConfig {configObserver = observer} =
393
+ establishConnection :: AppState -> IO ConnectionStatus
394
+ establishConnection appState@ AppState {stateObserver = observer} =
383
395
retrying retrySettings shouldRetry $
384
396
const $ flushPool appState >> getConnectionStatus
385
397
where
@@ -389,7 +401,7 @@ establishConnection appState config@AppConfig{configObserver=observer} =
389
401
390
402
getConnectionStatus :: IO ConnectionStatus
391
403
getConnectionStatus = do
392
- pgVersion <- usePool appState config (queryPgVersion False ) -- No need to prepare the query here, as the connection might not be established
404
+ pgVersion <- usePool appState (queryPgVersion False ) -- No need to prepare the query here, as the connection might not be established
393
405
case pgVersion of
394
406
Left e -> do
395
407
observer $ ConnectionPgVersionErrorObs e
@@ -418,12 +430,12 @@ establishConnection appState config@AppConfig{configObserver=observer} =
418
430
419
431
-- | Re-reads the config plus config options from the db
420
432
reReadConfig :: Bool -> AppState -> IO ()
421
- reReadConfig startingUp appState = do
422
- config @ AppConfig {configObserver = observer, .. } <- getConfig appState
433
+ reReadConfig startingUp appState@ AppState {stateObserver = observer} = do
434
+ AppConfig {.. } <- getConfig appState
423
435
pgVer <- getPgVersion appState
424
436
dbSettings <-
425
437
if configDbConfig then do
426
- qDbSettings <- usePool appState config (queryDbSettings (dumpQi <$> configDbPreConfig) configDbPreparedStatements)
438
+ qDbSettings <- usePool appState (queryDbSettings (dumpQi <$> configDbPreConfig) configDbPreparedStatements)
427
439
case qDbSettings of
428
440
Left e -> do
429
441
observer ConfigReadErrorObs
@@ -439,15 +451,15 @@ reReadConfig startingUp appState = do
439
451
pure mempty
440
452
(roleSettings, roleIsolationLvl) <-
441
453
if configDbConfig then do
442
- rSettings <- usePool appState config (queryRoleSettings pgVer configDbPreparedStatements)
454
+ rSettings <- usePool appState (queryRoleSettings pgVer configDbPreparedStatements)
443
455
case rSettings of
444
456
Left e -> do
445
457
observer $ QueryRoleSettingsErrorObs e
446
458
pure (mempty , mempty )
447
459
Right x -> pure x
448
460
else
449
461
pure mempty
450
- readAppConfig dbSettings configFilePath (Just configDbUri) roleSettings roleIsolationLvl observer >>= \ case
462
+ readAppConfig dbSettings configFilePath (Just configDbUri) roleSettings roleIsolationLvl >>= \ case
451
463
Left err ->
452
464
if startingUp then
453
465
panic err -- die on invalid config if the program is starting up
@@ -468,7 +480,7 @@ runListener conf@AppConfig{configDbChannelEnabled} appState = do
468
480
-- NOTIFY <db-channel> - with an empty payload - is done, it refills the schema
469
481
-- cache. It uses the connectionWorker in case the LISTEN connection dies.
470
482
listener :: AppState -> AppConfig -> IO ()
471
- listener appState conf @ AppConfig {configObserver = observer, .. } = do
483
+ listener appState@ AppState {stateObserver = observer} conf @ AppConfig { .. } = do
472
484
let dbChannel = toS configDbChannel
473
485
474
486
-- The listener has to wait for a signal from the connectionWorker.
0 commit comments