Problem statement
Subgraphs are blocked from resolving contributed entity fields until the parent resolver in the other subgraph yields.
(This makes sense because of the way resolvers receive parent values - but there's a opportunity for optimization.)
Example
Consider this schema + query:
Service A
type User @key(fields: "id") {
id: ID
name: String
}
type Query {
user(id: Int): User
}
Service B
type User @key(fields: "id") {
id: ID
profilePhoto: String
}
Now consider this query:
query {
user(id: 4) {
name
profilePhoto # comes from Service B
}
}
This query executes likes this 😱
# [user] -> [router] -> [Service A]
# ╰--------------> [Service B]
How can we avoid the waterfall to achieve this? 🤔
# [user] -> [router] -> [Service A]
# ╰> [Service B]
Breakdown
Our query plan will look like this:
QueryPlan {
Sequence {
Fetch(service: "legacy_monolith") {
{
user(id: 4) {
__typename
id
name
}
}
},
Flatten(path: "user") {
Fetch(service: "fancy_new_photos_subgraph") {
{ ... on User { __typename id } } =>
{
... on User {
profilePhoto
}
}
},
},
},
}
Pretty standard stuff.
Upon adding/migrating out the profilePhoto resolver to the new subgraph, teams notice "hey i have an extra waterfall", and they can't start hitting the database to fetch the profile photo until legacy_monolith finishes resolving - but there's no real dependency there.
With larger types, more fields and more subgraphs, we become more sensitive and exposed to this issue.
Very related discussion here https://gist.github.com/magicmark/cbda3eedf1255334caee357fde7680de
Proposal
In the above example, we have a very simplified example of Query.user(id: ID): User. In the initial request to the router, we already have the argument (id) that would be required to call __resolveReference in fancy_new_photos_subgraph.
This won't always be the case - e.g. Query.searchUser(name: String): User - this would require the searchUser to finish yielding before we can get an ID.
But many of our use cases, our @key(fields: ...) information is already available from the request object.
It would be cool to declare schema like this:
type User @key(fields: "id", hoistableFrom: "Query.user") {
id: ID!
profilePhoto: String
}
If all fields in the child subgraph query's selection set are being referenced via id with a matching hoistable argument, then the subgraph could call its __resolveReference in parallel with the query to the monolith.
Problem statement
Subgraphs are blocked from resolving contributed entity fields until the parent resolver in the other subgraph yields.
(This makes sense because of the way resolvers receive parent values - but there's a opportunity for optimization.)
Example
Consider this schema + query:
Service A
Service B
Now consider this query:
This query executes likes this 😱
How can we avoid the waterfall to achieve this? 🤔
Breakdown
Our query plan will look like this:
Pretty standard stuff.
Upon adding/migrating out the
profilePhotoresolver to the new subgraph, teams notice "hey i have an extra waterfall", and they can't start hitting the database to fetch the profile photo untillegacy_monolithfinishes resolving - but there's no real dependency there.With larger types, more fields and more subgraphs, we become more sensitive and exposed to this issue.
Very related discussion here https://gist.github.com/magicmark/cbda3eedf1255334caee357fde7680de
Proposal
In the above example, we have a very simplified example of
Query.user(id: ID): User. In the initial request to the router, we already have the argument (id) that would be required to call__resolveReferenceinfancy_new_photos_subgraph.This won't always be the case - e.g.
Query.searchUser(name: String): User- this would require thesearchUserto finish yielding before we can get an ID.But many of our use cases, our
@key(fields: ...)information is already available from the request object.It would be cool to declare schema like this:
If all fields in the child subgraph query's selection set are being referenced via
idwith a matchinghoistableargument, then the subgraph could call its__resolveReferencein parallel with the query to the monolith.