mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 08:02:15 +03:00
server: fix dirty read of schema cache
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8677 GitOrigin-RevId: 615bfb4dc8d22a46a87ceb76c5f89d608e88f97d
This commit is contained in:
parent
2e443401f3
commit
2814735a20
@ -88,7 +88,7 @@ import Hasura.Server.AppStateRef
|
||||
getAppContext,
|
||||
getRebuildableSchemaCacheWithVersion,
|
||||
getSchemaCache,
|
||||
withSchemaCacheUpdate,
|
||||
withSchemaCacheReadUpdate,
|
||||
)
|
||||
import Hasura.Server.Auth (AuthMode (..), UserAuthentication (..))
|
||||
import Hasura.Server.Compression
|
||||
@ -439,17 +439,17 @@ v1QueryHandler ::
|
||||
MonadGetApiTimeLimit m,
|
||||
UserInfoM m
|
||||
) =>
|
||||
(m (EncJSON, RebuildableSchemaCache) -> m EncJSON) ->
|
||||
((RebuildableSchemaCache -> m (EncJSON, RebuildableSchemaCache)) -> m EncJSON) ->
|
||||
RQLQuery ->
|
||||
m (HttpResponse EncJSON)
|
||||
v1QueryHandler schemaCacheRefUpdater query = do
|
||||
(liftEitherM . authorizeV1QueryApi query) =<< ask
|
||||
res <- bool (fst <$> action) (schemaCacheRefUpdater action) $ queryModifiesSchemaCache query
|
||||
schemaCache <- asks hcSchemaCache
|
||||
res <- bool (fst <$> action schemaCache) (schemaCacheRefUpdater action) $ queryModifiesSchemaCache query
|
||||
return $ HttpResponse res []
|
||||
where
|
||||
action = do
|
||||
action schemaCache = do
|
||||
appContext <- asks hcAppContext
|
||||
schemaCache <- asks hcSchemaCache
|
||||
runQuery
|
||||
appContext
|
||||
schemaCache
|
||||
@ -473,15 +473,14 @@ v1MetadataHandler ::
|
||||
MonadGetApiTimeLimit m,
|
||||
UserInfoM m
|
||||
) =>
|
||||
(m (EncJSON, RebuildableSchemaCache) -> m EncJSON) ->
|
||||
((RebuildableSchemaCache -> m (EncJSON, RebuildableSchemaCache)) -> m EncJSON) ->
|
||||
RQLMetadata ->
|
||||
m (HttpResponse EncJSON)
|
||||
v1MetadataHandler schemaCacheRefUpdater query = Tracing.newSpan "Metadata" $ do
|
||||
(liftEitherM . authorizeV1MetadataApi query) =<< ask
|
||||
appContext <- asks hcAppContext
|
||||
schemaCache <- asks hcSchemaCache
|
||||
r <-
|
||||
schemaCacheRefUpdater $
|
||||
schemaCacheRefUpdater $ \schemaCache ->
|
||||
runMetadataQuery
|
||||
appContext
|
||||
schemaCache
|
||||
@ -503,19 +502,19 @@ v2QueryHandler ::
|
||||
ProvidesNetwork m,
|
||||
UserInfoM m
|
||||
) =>
|
||||
(m (EncJSON, RebuildableSchemaCache) -> m EncJSON) ->
|
||||
((RebuildableSchemaCache -> m (EncJSON, RebuildableSchemaCache)) -> m EncJSON) ->
|
||||
V2Q.RQLQuery ->
|
||||
m (HttpResponse EncJSON)
|
||||
v2QueryHandler schemaCacheRefUpdater query = Tracing.newSpan "v2 Query" $ do
|
||||
schemaCache <- asks hcSchemaCache
|
||||
(liftEitherM . authorizeV2QueryApi query) =<< ask
|
||||
res <-
|
||||
bool (fst <$> dbAction) (schemaCacheRefUpdater dbAction) $
|
||||
bool (fst <$> dbAction schemaCache) (schemaCacheRefUpdater dbAction) $
|
||||
V2Q.queryModifiesSchema query
|
||||
return $ HttpResponse res []
|
||||
where
|
||||
-- Hit postgres
|
||||
dbAction = do
|
||||
schemaCache <- asks hcSchemaCache
|
||||
dbAction schemaCache = do
|
||||
appContext <- asks hcAppContext
|
||||
V2Q.runQuery
|
||||
appContext
|
||||
@ -923,7 +922,7 @@ httpApp setupHook appStateRef AppEnv {..} consoleType ekgStore = do
|
||||
|
||||
-- Note: we create a schema cache updater function, to restrict the access
|
||||
-- to 'AppStateRef' inside the request handlers
|
||||
let schemaCacheUpdater = withSchemaCacheUpdate appStateRef logger Nothing
|
||||
let schemaCacheUpdater = withSchemaCacheReadUpdate appStateRef logger Nothing
|
||||
|
||||
Spock.post "v1/graphql/explain" $ do
|
||||
onlyWhenApiEnabled isMetadataEnabled appStateRef gqlExplainAction
|
||||
|
@ -18,6 +18,7 @@ module Hasura.Server.AppStateRef
|
||||
readAppContextRef,
|
||||
getAppContext,
|
||||
logInconsistentMetadata,
|
||||
withSchemaCacheReadUpdate,
|
||||
)
|
||||
where
|
||||
|
||||
@ -136,6 +137,42 @@ withSchemaCacheUpdate (AppStateRef lock cacheRef metadataVersionGauge) logger mL
|
||||
|
||||
pure res
|
||||
|
||||
withSchemaCacheReadUpdate ::
|
||||
(MonadIO m, MonadBaseControl IO m) =>
|
||||
(AppStateRef impl) ->
|
||||
L.Logger L.Hasura ->
|
||||
Maybe (STM.TVar Bool) ->
|
||||
(RebuildableSchemaCache -> m (a, RebuildableSchemaCache)) ->
|
||||
m a
|
||||
withSchemaCacheReadUpdate (AppStateRef lock cacheRef metadataVersionGauge) logger mLogCheckerTVar action =
|
||||
withMVarMasked lock $ const do
|
||||
(rebuildableSchemaCache, _) <- asSchemaCache <$> liftIO (readIORef cacheRef)
|
||||
(!res, !newSC) <- action rebuildableSchemaCache
|
||||
liftIO do
|
||||
-- update schemacache in IO reference
|
||||
modifyIORef' cacheRef $ \appState ->
|
||||
let !newVer = incSchemaCacheVer (snd $ asSchemaCache appState)
|
||||
in appState {asSchemaCache = (newSC, newVer)}
|
||||
|
||||
-- update metric with new metadata version
|
||||
updateMetadataVersionGauge metadataVersionGauge newSC
|
||||
|
||||
let inconsistentObjectsList = scInconsistentObjs $ lastBuiltSchemaCache newSC
|
||||
logInconsistentMetadata' = logInconsistentMetadata logger inconsistentObjectsList
|
||||
-- log any inconsistent objects only once and not everytime this method is called
|
||||
case mLogCheckerTVar of
|
||||
Nothing -> logInconsistentMetadata'
|
||||
Just logCheckerTVar -> do
|
||||
logCheck <- STM.readTVarIO logCheckerTVar
|
||||
if null inconsistentObjectsList && logCheck
|
||||
then do
|
||||
STM.atomically $ STM.writeTVar logCheckerTVar False
|
||||
else do
|
||||
unless (logCheck || null inconsistentObjectsList) $ do
|
||||
STM.atomically $ STM.writeTVar logCheckerTVar True
|
||||
logInconsistentMetadata'
|
||||
pure res
|
||||
|
||||
-- | Read the contents of the 'AppStateRef' to get the latest 'RebuildableAppContext'
|
||||
readAppContextRef :: AppStateRef impl -> IO (RebuildableAppContext impl)
|
||||
readAppContextRef scRef = asAppCtx <$> readIORef (_scrCache scRef)
|
||||
|
Loading…
Reference in New Issue
Block a user