Skip to content
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

Query Planner Transitive Dependency POC #922

Draft
wants to merge 7 commits into
base: version-0.x
Choose a base branch
from

Conversation

joshmarsh
Copy link

Summary

This POC was used to better understand the work required to enable transitive dependency support in the query planner. The scope is limited to satisfying the basic use case expressed in this unit test.

Approach

The solution is to construct a dependency graph from the GraphQLSchema, and then traverse said graph to find the subset of nodes that are relevant to fulfilling an operation (i.e. query or mutation). We then sort the subset of relevant nodes into a legal order of execution and map them into a QueryPlan. See below for a more detailed breakdown of each step:

1. Construct a dependency graph from the GraphQLSchema

We implemented the dependency graph as a separate class, which contains an adjacency set and a number of utility methods. The fromSchema method instantiates this class from a GraphQLSchema by iterating over the fields in its' TypeMap and creating an edge for each dependency found in field.extensions.federation.requires. We treat key fields as dependencies in a similar manner.

While this is done in the query planner for simplicity, it could be performed ahead of time whenever the schema changes.

2. Traverse the schema dependency graph to find the subset of nodes that are needed to fulfill an operation

Our mental model was informed by this comment. As it pertains to this problem, the schema can be thought of as a graph with both structural and dependency edges. Structural edges define the structure of the schema (e.g. Query.getA: A -> fieldOfA, fieldOfA: B -> fieldOfB, etc.). Dependency edges are defined by @requires and @key. Given an operation and a schema, and assuming that the operation is a structural subset of the schema, we can find the nodes needed to fulfil said operation by following the operation's structural edges and the schema's dependency edges.

getDependencySubgraph does this by traversing the fiedDef (schema) and fieldNode (operation) trees in parallel. A seperate data structure maintains a mapping between nodes and paths needed later to define the mergeAt property in QueryPlan nodes.

3. Sort the subgraph into a legal order of execution

getSortedNodes is currently implemented using a simple toposorting library. This should be expanded in the future to support parallelism and batching requests to the same service.

4. Map sorted nodes into a QueryPlan

This is performed by combining snippets from a QueryPlan object, since properly constructing the QueryPlan was not the
focus of this POC.

@apollo-cla
Copy link

@joshmarsh: Thank you for submitting a pull request! Before we can merge it, you'll need to sign the Apollo Contributor License Agreement here: https://contribute.apollographql.com/

@netlify
Copy link

netlify bot commented Jul 30, 2021

👷 Deploy request for apollo-federation-docs pending review.
Visit the deploys page to approve it

🔨 Explore the source changes: 22d502c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants