mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +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,
|
getAppContext,
|
||||||
getRebuildableSchemaCacheWithVersion,
|
getRebuildableSchemaCacheWithVersion,
|
||||||
getSchemaCache,
|
getSchemaCache,
|
||||||
withSchemaCacheUpdate,
|
withSchemaCacheReadUpdate,
|
||||||
)
|
)
|
||||||
import Hasura.Server.Auth (AuthMode (..), UserAuthentication (..))
|
import Hasura.Server.Auth (AuthMode (..), UserAuthentication (..))
|
||||||
import Hasura.Server.Compression
|
import Hasura.Server.Compression
|
||||||
@ -439,17 +439,17 @@ v1QueryHandler ::
|
|||||||
MonadGetApiTimeLimit m,
|
MonadGetApiTimeLimit m,
|
||||||
UserInfoM m
|
UserInfoM m
|
||||||
) =>
|
) =>
|
||||||
(m (EncJSON, RebuildableSchemaCache) -> m EncJSON) ->
|
((RebuildableSchemaCache -> m (EncJSON, RebuildableSchemaCache)) -> m EncJSON) ->
|
||||||
RQLQuery ->
|
RQLQuery ->
|
||||||
m (HttpResponse EncJSON)
|
m (HttpResponse EncJSON)
|
||||||
v1QueryHandler schemaCacheRefUpdater query = do
|
v1QueryHandler schemaCacheRefUpdater query = do
|
||||||
(liftEitherM . authorizeV1QueryApi query) =<< ask
|
(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 []
|
return $ HttpResponse res []
|
||||||
where
|
where
|
||||||
action = do
|
action schemaCache = do
|
||||||
appContext <- asks hcAppContext
|
appContext <- asks hcAppContext
|
||||||
schemaCache <- asks hcSchemaCache
|
|
||||||
runQuery
|
runQuery
|
||||||
appContext
|
appContext
|
||||||
schemaCache
|
schemaCache
|
||||||
@ -473,15 +473,14 @@ v1MetadataHandler ::
|
|||||||
MonadGetApiTimeLimit m,
|
MonadGetApiTimeLimit m,
|
||||||
UserInfoM m
|
UserInfoM m
|
||||||
) =>
|
) =>
|
||||||
(m (EncJSON, RebuildableSchemaCache) -> m EncJSON) ->
|
((RebuildableSchemaCache -> m (EncJSON, RebuildableSchemaCache)) -> m EncJSON) ->
|
||||||
RQLMetadata ->
|
RQLMetadata ->
|
||||||
m (HttpResponse EncJSON)
|
m (HttpResponse EncJSON)
|
||||||
v1MetadataHandler schemaCacheRefUpdater query = Tracing.newSpan "Metadata" $ do
|
v1MetadataHandler schemaCacheRefUpdater query = Tracing.newSpan "Metadata" $ do
|
||||||
(liftEitherM . authorizeV1MetadataApi query) =<< ask
|
(liftEitherM . authorizeV1MetadataApi query) =<< ask
|
||||||
appContext <- asks hcAppContext
|
appContext <- asks hcAppContext
|
||||||
schemaCache <- asks hcSchemaCache
|
|
||||||
r <-
|
r <-
|
||||||
schemaCacheRefUpdater $
|
schemaCacheRefUpdater $ \schemaCache ->
|
||||||
runMetadataQuery
|
runMetadataQuery
|
||||||
appContext
|
appContext
|
||||||
schemaCache
|
schemaCache
|
||||||
@ -503,19 +502,19 @@ v2QueryHandler ::
|
|||||||
ProvidesNetwork m,
|
ProvidesNetwork m,
|
||||||
UserInfoM m
|
UserInfoM m
|
||||||
) =>
|
) =>
|
||||||
(m (EncJSON, RebuildableSchemaCache) -> m EncJSON) ->
|
((RebuildableSchemaCache -> m (EncJSON, RebuildableSchemaCache)) -> m EncJSON) ->
|
||||||
V2Q.RQLQuery ->
|
V2Q.RQLQuery ->
|
||||||
m (HttpResponse EncJSON)
|
m (HttpResponse EncJSON)
|
||||||
v2QueryHandler schemaCacheRefUpdater query = Tracing.newSpan "v2 Query" $ do
|
v2QueryHandler schemaCacheRefUpdater query = Tracing.newSpan "v2 Query" $ do
|
||||||
|
schemaCache <- asks hcSchemaCache
|
||||||
(liftEitherM . authorizeV2QueryApi query) =<< ask
|
(liftEitherM . authorizeV2QueryApi query) =<< ask
|
||||||
res <-
|
res <-
|
||||||
bool (fst <$> dbAction) (schemaCacheRefUpdater dbAction) $
|
bool (fst <$> dbAction schemaCache) (schemaCacheRefUpdater dbAction) $
|
||||||
V2Q.queryModifiesSchema query
|
V2Q.queryModifiesSchema query
|
||||||
return $ HttpResponse res []
|
return $ HttpResponse res []
|
||||||
where
|
where
|
||||||
-- Hit postgres
|
-- Hit postgres
|
||||||
dbAction = do
|
dbAction schemaCache = do
|
||||||
schemaCache <- asks hcSchemaCache
|
|
||||||
appContext <- asks hcAppContext
|
appContext <- asks hcAppContext
|
||||||
V2Q.runQuery
|
V2Q.runQuery
|
||||||
appContext
|
appContext
|
||||||
@ -923,7 +922,7 @@ httpApp setupHook appStateRef AppEnv {..} consoleType ekgStore = do
|
|||||||
|
|
||||||
-- Note: we create a schema cache updater function, to restrict the access
|
-- Note: we create a schema cache updater function, to restrict the access
|
||||||
-- to 'AppStateRef' inside the request handlers
|
-- to 'AppStateRef' inside the request handlers
|
||||||
let schemaCacheUpdater = withSchemaCacheUpdate appStateRef logger Nothing
|
let schemaCacheUpdater = withSchemaCacheReadUpdate appStateRef logger Nothing
|
||||||
|
|
||||||
Spock.post "v1/graphql/explain" $ do
|
Spock.post "v1/graphql/explain" $ do
|
||||||
onlyWhenApiEnabled isMetadataEnabled appStateRef gqlExplainAction
|
onlyWhenApiEnabled isMetadataEnabled appStateRef gqlExplainAction
|
||||||
|
@ -18,6 +18,7 @@ module Hasura.Server.AppStateRef
|
|||||||
readAppContextRef,
|
readAppContextRef,
|
||||||
getAppContext,
|
getAppContext,
|
||||||
logInconsistentMetadata,
|
logInconsistentMetadata,
|
||||||
|
withSchemaCacheReadUpdate,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
@ -136,6 +137,42 @@ withSchemaCacheUpdate (AppStateRef lock cacheRef metadataVersionGauge) logger mL
|
|||||||
|
|
||||||
pure res
|
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'
|
-- | Read the contents of the 'AppStateRef' to get the latest 'RebuildableAppContext'
|
||||||
readAppContextRef :: AppStateRef impl -> IO (RebuildableAppContext impl)
|
readAppContextRef :: AppStateRef impl -> IO (RebuildableAppContext impl)
|
||||||
readAppContextRef scRef = asAppCtx <$> readIORef (_scrCache scRef)
|
readAppContextRef scRef = asAppCtx <$> readIORef (_scrCache scRef)
|
||||||
|
Loading…
Reference in New Issue
Block a user