-
Notifications
You must be signed in to change notification settings - Fork 251
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Gateway(experimental): compress downstream requests via generat… (apo…
…llographql/apollo-server#3791) For large queries (esp. ones that leverage many nested fragments), queries to downstream services can blow up astronomically because the gateway expands everything during a downstream request. i.e. we've seen a ~700 line query become over 200k lines due to this expansion. This commit has the gateway build fragments on the fly for the selection sets that it encounters and reuses them whenever possible. This is an initial and simple implementation, but with tangible wins. This is currently being landed as an experimental feature that is disabled by default. To enable, simply add to your gateway config: experimental_autoFragmentization: true Apollo-Orig-Commit-AS: apollographql/apollo-server@c48c0a3
- Loading branch information
1 parent
e481999
commit 19c83bb
Showing
9 changed files
with
525 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -856,25 +856,336 @@ describe('buildQueryPlan', () => { | |
const queryPlan = buildQueryPlan(buildOperationContext(schema, query)); | ||
|
||
expect(queryPlan).toMatchInlineSnapshot(` | ||
QueryPlan { | ||
Fetch(service: "product") { | ||
{ | ||
product(upc: "") { | ||
__typename | ||
... on Book { | ||
QueryPlan { | ||
Fetch(service: "product") { | ||
{ | ||
product(upc: "") { | ||
__typename | ||
... on Book { | ||
details { | ||
country | ||
} | ||
} | ||
... on Furniture { | ||
details { | ||
country | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
} | ||
`); | ||
}); | ||
|
||
describe(`experimental compression to downstream services`, () => { | ||
it(`should generate fragments internally to downstream requests`, () => { | ||
const query = gql` | ||
query { | ||
topReviews { | ||
body | ||
author | ||
product { | ||
name | ||
price | ||
details { | ||
country | ||
} | ||
} | ||
... on Furniture { | ||
} | ||
} | ||
`; | ||
|
||
const queryPlan = buildQueryPlan( | ||
buildOperationContext(schema, query, undefined), | ||
{ autoFragmentization: true }, | ||
); | ||
|
||
expect(queryPlan).toMatchInlineSnapshot(` | ||
QueryPlan { | ||
Sequence { | ||
Fetch(service: "reviews") { | ||
{ | ||
topReviews { | ||
...__QueryPlanFragment_1__ | ||
} | ||
} | ||
fragment __QueryPlanFragment_1__ on Review { | ||
body | ||
author | ||
product { | ||
...__QueryPlanFragment_0__ | ||
} | ||
} | ||
fragment __QueryPlanFragment_0__ on Product { | ||
__typename | ||
... on Book { | ||
__typename | ||
isbn | ||
} | ||
... on Furniture { | ||
__typename | ||
upc | ||
} | ||
} | ||
}, | ||
Parallel { | ||
Sequence { | ||
Flatten(path: "[email protected]") { | ||
Fetch(service: "books") { | ||
{ | ||
... on Book { | ||
__typename | ||
isbn | ||
} | ||
} => | ||
{ | ||
... on Book { | ||
__typename | ||
isbn | ||
title | ||
year | ||
} | ||
} | ||
}, | ||
}, | ||
Flatten(path: "[email protected]") { | ||
Fetch(service: "product") { | ||
{ | ||
... on Book { | ||
__typename | ||
isbn | ||
title | ||
year | ||
} | ||
} => | ||
{ | ||
... on Book { | ||
name | ||
} | ||
} | ||
}, | ||
}, | ||
}, | ||
Flatten(path: "[email protected]") { | ||
Fetch(service: "product") { | ||
{ | ||
... on Furniture { | ||
__typename | ||
upc | ||
} | ||
... on Book { | ||
__typename | ||
isbn | ||
} | ||
} => | ||
{ | ||
... on Furniture { | ||
name | ||
price | ||
details { | ||
country | ||
} | ||
} | ||
... on Book { | ||
price | ||
details { | ||
country | ||
} | ||
} | ||
} | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
`); | ||
}); | ||
|
||
it(`shouldn't generate fragments for selection sets of length 2 or less`, () => { | ||
const query = gql` | ||
query { | ||
topReviews { | ||
body | ||
author | ||
} | ||
} | ||
`; | ||
|
||
const queryPlan = buildQueryPlan( | ||
buildOperationContext(schema, query, undefined), | ||
{ autoFragmentization: true }, | ||
); | ||
|
||
expect(queryPlan).toMatchInlineSnapshot(` | ||
QueryPlan { | ||
Fetch(service: "reviews") { | ||
{ | ||
topReviews { | ||
body | ||
author | ||
} | ||
} | ||
}, | ||
} | ||
`); | ||
}); | ||
|
||
it(`should generate fragments for selection sets of length 3 or greater`, () => { | ||
const query = gql` | ||
query { | ||
topReviews { | ||
id | ||
body | ||
author | ||
} | ||
} | ||
`; | ||
|
||
const queryPlan = buildQueryPlan( | ||
buildOperationContext(schema, query, undefined), | ||
{ autoFragmentization: true }, | ||
); | ||
|
||
expect(queryPlan).toMatchInlineSnapshot(` | ||
QueryPlan { | ||
Fetch(service: "reviews") { | ||
{ | ||
topReviews { | ||
...__QueryPlanFragment_0__ | ||
} | ||
} | ||
fragment __QueryPlanFragment_0__ on Review { | ||
id | ||
body | ||
author | ||
} | ||
}, | ||
} | ||
`); | ||
}); | ||
|
||
it(`should generate fragments correctly when aliases are used`, () => { | ||
const query = gql` | ||
query { | ||
reviews: topReviews { | ||
content: body | ||
author | ||
product { | ||
name | ||
cost: price | ||
details { | ||
country | ||
origin: country | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
} | ||
`); | ||
`; | ||
|
||
const queryPlan = buildQueryPlan( | ||
buildOperationContext(schema, query, undefined), | ||
{ autoFragmentization: true }, | ||
); | ||
|
||
expect(queryPlan).toMatchInlineSnapshot(` | ||
QueryPlan { | ||
Sequence { | ||
Fetch(service: "reviews") { | ||
{ | ||
reviews: topReviews { | ||
...__QueryPlanFragment_1__ | ||
} | ||
} | ||
fragment __QueryPlanFragment_1__ on Review { | ||
content: body | ||
author | ||
product { | ||
...__QueryPlanFragment_0__ | ||
} | ||
} | ||
fragment __QueryPlanFragment_0__ on Product { | ||
__typename | ||
... on Book { | ||
__typename | ||
isbn | ||
} | ||
... on Furniture { | ||
__typename | ||
upc | ||
} | ||
} | ||
}, | ||
Parallel { | ||
Sequence { | ||
Flatten(path: "[email protected]") { | ||
Fetch(service: "books") { | ||
{ | ||
... on Book { | ||
__typename | ||
isbn | ||
} | ||
} => | ||
{ | ||
... on Book { | ||
__typename | ||
isbn | ||
title | ||
year | ||
} | ||
} | ||
}, | ||
}, | ||
Flatten(path: "[email protected]") { | ||
Fetch(service: "product") { | ||
{ | ||
... on Book { | ||
__typename | ||
isbn | ||
title | ||
year | ||
} | ||
} => | ||
{ | ||
... on Book { | ||
name | ||
} | ||
} | ||
}, | ||
}, | ||
}, | ||
Flatten(path: "[email protected]") { | ||
Fetch(service: "product") { | ||
{ | ||
... on Furniture { | ||
__typename | ||
upc | ||
} | ||
... on Book { | ||
__typename | ||
isbn | ||
} | ||
} => | ||
{ | ||
... on Furniture { | ||
name | ||
cost: price | ||
details { | ||
origin: country | ||
} | ||
} | ||
... on Book { | ||
cost: price | ||
details { | ||
origin: country | ||
} | ||
} | ||
} | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
`); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.