-
Notifications
You must be signed in to change notification settings - Fork 264
Description
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.