apollographql / apollographql/federation

Update Query Planner to support Named Fragment generation.

Open
#790 6 comments 0 reactions 0 assignees View on GitHub
component/query routing
Dominant language
TypeScript
Stars
725
Forks
276
Avg merge
1h 47m
Merged PRs (30d)
1

Description

We have a few graphs that we have/are building that are pretty complex in the sense that they have more that a few complex types(implementing interfaces or Unions) with joins in between each other. For the most part we've gotten this going quite well accept for some of the limitations around interfaces and unions([#336](https://github.com/apollographql/federation/issues/336)). My issue I have now is not that I can't get it to work, but that its not very performant.

Take for example that I have 2 services.
**Service A**
```
Query {
vehicles: [Vehicle]
}

interface Vehicle {
id: ID!
engine: Engine
}
type Car implements Vehicle @key(fields:"id") {
id: ID!
...
}
type Truck implements Vehicle @key(fields:"id") {
id: ID!
...
}

extend interface Engine @key(fields: "id") {
id: Int! @external
}

extend type GasEngine implements Engine @key(fields:"id") {
id: ID! @external
}

extend type ElictricEngine implements Engine @key(fields:"id") {
id: ID! @external
}
```
**Service B**
```
interface Engine {
id: ID
}

type GasEngine implements Engine @key(fields:"id") {
id: ID!
...
}

type ElictricEngine implements Engine @key(fields:"id") {
id: ID!
...
}
```

Now when I run a simple query to return vehicles
```
query {
vehicles {
id
engine {
id
}
}
}
```
The federated graph generates the query with type expansion using inline fragments like the following to service A.
```
query {
vehicles {
__typename
... on Car {
__typename
id
engine {
__typename
... on GasEngine {
__typename
id
}
... on ElectricEngine {
__typename
id
}
}
}
... on Truck {
__typename
id
engine {
__typename
... on GasEngine {
__typename
id
}
... on ElectricEngine {
__typename
id
}
}
}
}
}
```
For this example, this doesn't look too bad if you take into consideration that there are 2 vehicle types and 2 engine types which means it's generating type expansion queries with 4(2x2) sub-types, however in our case we have more than 2 layers deep and 30+ sub-types in each so if I run the same calculation with 3 layers and 30 sub-types you get 27,000(30x30x30) instances of type expansion in the query. If you take into consideration that a single type takes approximately 100 bytes to post, then that means to execute this query we will have to post 2.7MB to Service A.

My proposal is to modify the query generator to use Named fragments instead of inline fragments like the following.

```
fragment EngineFragment on Engine {
__typename
... on GasEngine {
__typename
id
}
... on ElectricEngine {
__typename
id
}
}
fragment VehicleFragment on Vehicle {
__typename
... on Car {
__typename
id
engine {
...EngineFragment
}
}
... on Truck {
__typename
id
engine {
...EngineFragment
}
}
}
query {
vehicles {
...VehicleFragement
}
}
```
This would eliminate a large portion of the duplication and shrink the query significantly. I wanted to propose this idea for a few purposes.

1. Maybe we're doing something wrong with federation that is causing the excessive type expansion.
2. Pass this idea through the community to see what the interest is and whether this idea is worth following.
3. Communicate with the Technical leads on this project to determine how best to approach this change.
4. Gather information on this project as I have not worked in it before.

I hope I have covered all the bases in my explanation, but if you have questions, please feel free to post replies here as I will be monitoring this thread closely. Thank you.

Contributor guide

Open the contributing guide

Research direction

Start by locating the query planner and query generator responsible for expanding interfaces and unions, then compare their current generated queries with the named-fragment proposal in this issue. Done means the planner can reuse named fragments to avoid repeated type-expansion selections while preserving the requested result shape.

Written by the indexing model from the issue text.

Assessment

Tech stack
graphql, typescript
Domain
api, backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.