@@ -22,6 +22,7 @@ module PostgREST.AppState
2222 , usePool
2323 , readInDbConfig
2424 , schemaCacheLoader
25+ , waitForSchemaCacheLoaded
2526 , getObserver
2627 , isLoaded
2728 , isPending
@@ -76,7 +77,7 @@ data AppState = AppState
7677 -- | Schema cache
7778 , stateSchemaCache :: IORef (Maybe SchemaCache )
7879 -- | The schema cache status
79- , stateSCacheStatus :: IORef SchemaCacheStatus
80+ , stateSCacheStatus :: SchemaCacheStatus
8081 -- | State of the LISTEN channel
8182 , stateIsListenerOn :: IORef Bool
8283 -- | starts the connection worker with a debounce
@@ -99,11 +100,11 @@ data AppState = AppState
99100 , stateMetrics :: Metrics. MetricsState
100101 }
101102
102- -- | Schema cache status
103- data SchemaCacheStatus
104- = SCLoaded
105- | SCPending
106- deriving Eq
103+ -- | Schema cache status.
104+ -- Empty means pending and full means loaded.
105+ newtype SchemaCacheStatus = SchemaCacheStatus
106+ { getSCStatusMVar :: MVar ()
107+ }
107108
108109init :: AppConfig -> IO AppState
109110init conf@ AppConfig {configLogLevel, configDbPoolSize} = do
@@ -122,7 +123,7 @@ initWithPool pool conf loggerState metricsState observer = do
122123 appState <- AppState pool
123124 <$> newIORef minimumPgVersion -- assume we're in a supported version when starting, this will be corrected on a later step
124125 <*> newIORef Nothing
125- <*> newIORef SCPending
126+ <*> newSchemaCacheStatus
126127 <*> newIORef False
127128 <*> pure (pure () )
128129 <*> newIORef conf
@@ -240,6 +241,9 @@ putSchemaCache appState = atomicWriteIORef (stateSchemaCache appState)
240241schemaCacheLoader :: AppState -> IO ()
241242schemaCacheLoader = debouncedSCacheLoader
242243
244+ waitForSchemaCacheLoaded :: AppState -> IO ()
245+ waitForSchemaCacheLoaded = void . readMVar . getSCStatusMVar . stateSCacheStatus
246+
243247getNextDelay :: AppState -> IO Int
244248getNextDelay = readIORef . stateNextDelay
245249
@@ -277,18 +281,15 @@ putIsListenerOn = atomicWriteIORef . stateIsListenerOn
277281
278282isLoaded :: AppState -> IO Bool
279283isLoaded x = do
280- scacheStatus <- readIORef $ stateSCacheStatus x
284+ scacheLoaded <- isSchemaCacheLoaded x
281285 connEstablished <- isConnEstablished x
282- return $ scacheStatus == SCLoaded && connEstablished
286+ return $ scacheLoaded && connEstablished
283287
284288isPending :: AppState -> IO Bool
285289isPending x = do
286- scacheStatus <- readIORef $ stateSCacheStatus x
290+ scacheLoaded <- isSchemaCacheLoaded x
287291 connEstablished <- isConnEstablished x
288- return $ scacheStatus == SCPending || not connEstablished
289-
290- putSCacheStatus :: AppState -> SchemaCacheStatus -> IO ()
291- putSCacheStatus = atomicWriteIORef . stateSCacheStatus
292+ return $ not scacheLoaded || not connEstablished
292293
293294getObserver :: AppState -> ObservationHandler
294295getObserver = stateObserver
@@ -347,19 +348,19 @@ retryingSchemaCacheLoad appState@AppState{stateObserver=observer, stateMainThrea
347348 timeItT $ usePool appState (transaction SQL. ReadCommitted SQL. Read $ querySchemaCache conf)
348349 case result of
349350 Left e -> do
350- putSCacheStatus appState SCPending
351+ markSchemaCachePending appState
351352 putSchemaCache appState Nothing
352353 observer $ SchemaCacheErrorObs configDbSchemas configDbExtraSearchPath e
353354 return Nothing
354355
355356 Right sCache -> do
356357 -- IMPORTANT: While the pending schema cache state starts from running the above querySchemaCache, only at this stage we block API requests due to the usage of an
357- -- IORef on putSchemaCache. This is why SCacheStatus is put at SCPending here to signal the Admin server (using isPending) that we're on a recovery state.
358- putSCacheStatus appState SCPending
358+ -- IORef on putSchemaCache. This is why schema cache status is marked as pending here to signal the Admin server (using isPending) that we're on a recovery state.
359+ markSchemaCachePending appState
359360 putSchemaCache appState $ Just sCache
360361 observer $ SchemaCacheQueriedObs resultTime
361362 observer . uncurry SchemaCacheLoadedObs =<< timeItT (evaluate $ showSummary sCache)
362- putSCacheStatus appState SCLoaded
363+ markSchemaCacheLoaded appState
363364 return $ Just sCache
364365
365366 shouldRetry :: RetryStatus -> (Maybe PgVersion , Maybe SchemaCache ) -> IO Bool
@@ -375,6 +376,18 @@ retryingSchemaCacheLoad appState@AppState{stateObserver=observer, stateMainThrea
375376
376377 oneSecondInUs = 1000000 -- one second in microseconds
377378
379+ newSchemaCacheStatus :: IO SchemaCacheStatus
380+ newSchemaCacheStatus = SchemaCacheStatus <$> newEmptyMVar
381+
382+ markSchemaCachePending :: AppState -> IO ()
383+ markSchemaCachePending = void . tryTakeMVar . getSCStatusMVar . stateSCacheStatus
384+
385+ markSchemaCacheLoaded :: AppState -> IO ()
386+ markSchemaCacheLoaded = void . (`tryPutMVar` () ) . getSCStatusMVar . stateSCacheStatus
387+
388+ isSchemaCacheLoaded :: AppState -> IO Bool
389+ isSchemaCacheLoaded = fmap not . isEmptyMVar . getSCStatusMVar . stateSCacheStatus
390+
378391-- | Reads the in-db config and reads the config file again
379392-- | We don't retry reading the in-db config after it fails immediately, because it could have user errors. We just report the error and continue.
380393readInDbConfig :: Bool -> AppState -> IO ()
0 commit comments