-
Notifications
You must be signed in to change notification settings - Fork 2.8k
server: heterogeneous execution of GraphQL queries #5869
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
server: heterogeneous execution of GraphQL queries #5869
Conversation
The "locality" field needs to get additional options. This commit takes an opinionated view on this which we should discuss further.
Co-authored-by: Alexis King <lexi.lambda@gmail.com>
Co-authored-by: Alexis King <lexi.lambda@gmail.com>
|
@lexi-lambda Thank you very much for the thorough review! |
lexi-lambda
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many thanks for the prompt changes; I think this PR generally looks good to me now. I think it would be nice to find some way to deduplicate all the tricky query execution logic and parameterize it over the transport mechanism, but I’m happy to have a call to discuss that, since it seems possibly subtle.
| | GQPreExecError ![J.Value] | ||
| | GQExecError ![J.Value] | ||
| deriving (Show, Eq, Functor, Foldable, Traversable) | ||
| type GQResult a = ExceptT GQExecError Identity a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ExceptT over Identity is basically just an awkward version of Either. Is there a reason we need ExceptT here specifically?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
| -- spent in the PG query; for telemetry. | ||
| runQueryDB reqId (query, queryParsed) asts _userInfo (tx, genSql) = do | ||
| -> Text | ||
| -> (Tracing.TraceT (LazyTx QErr) EncJSON, Maybe EQ.GeneratedSqlMap) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we ever actually call logQueryLog with a map containing more than one entry in it? If not, I’d be in favor of just changing the signature—I don’t think avoiding the tiny bit of extra downstream effort to update to the new interface is worth the misleading API. (Of course, if we do call it with more than one value somewhere, please point me to it!)
| prepArgs = fst <$> IntMap.elems args | ||
| in (, Just ps) $ case remoteJoinsM of | ||
| Nothing -> do | ||
| Tracing.trace "Postgres" . (runExtractProfile ep =<<) . liftTx $ asSingleRowJsonResp (instrument q) prepArgs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is another instance of an operator section involving =<<, which I missed on the first pass, but I personally find confusing. There’s a lot going on all on one line here. I recognize this kind of thing is subjective, but I happen to prefer a formulation like this:
| Tracing.trace "Postgres" . (runExtractProfile ep =<<) . liftTx $ asSingleRowJsonResp (instrument q) prepArgs | |
| Tracing.trace "Postgres" $ runExtractProfile ep =<< liftTx do | |
| asSingleRowJsonResp (instrument q) prepArgs |
This puts all the “wrapping bits” on a separate line from the actual interesting functionality being executed. Another approach I use sometimes when the “wrapping bits” get complicated is something like this:
| Tracing.trace "Postgres" . (runExtractProfile ep =<<) . liftTx $ asSingleRowJsonResp (instrument q) prepArgs | |
| asSingleRowJsonResp (instrument q) prepArgs | |
| & liftTx | |
| >>= runExtractProfile ep | |
| & Tracing.trace "Postgres" |
Which has sort of a “pipeline” flavor to it. This works out nicely because & and >>= are both infixl 1. But in this particular case, I’m not sure that it’s actually better; most of the parts of the “pipeline” are pretty boring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
tirumaraiselvan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changelog
Description
The server could already execute queries that either fetch data from the database, or, through remotes, from other GraphQL servers. This PR also enables mixing such data sources within one query. So one can have queries such as
where the articles are fetched from the database, and the weather is fetched from a remote server.
Changelog
CHANGELOG.mdis updated with user-facing content relevant to this PR. If no changelog is required, then add theno-changelog-requiredlabel.Affected components
Related Issues
This PR depends on #5865.
Solution and Design
For an incoming query we already used to generate an "execution plan" which specified how to execute that query. However, so far an execution plan has been a single execution step, where an execution step can be of database-type, of remote-type or of "raw"-type (used for introspection). This PR changes the type of execution plans to be an insertion-ordered hashmap of execution steps
where the key corresponds to a root field name.
Steps to test and verify
A test suite is included.
Limitations, known bugs & workarounds
Although this PR allows to mix root fields originating from different data sources, it does not allow joining between them arbitrarily beyond the existing "remote joins" that load relationships from Postgres to a remote. Such "generalized joins" are part of future work.
Server checklist
Catalog upgrade
Does this PR change Hasura Catalog version?
Metadata
Does this PR add a new Metadata feature?
GraphQL
Breaking changes