Skip to content

Commit

Permalink
Add generate_query_fragments configuration option (#4885)
Browse files Browse the repository at this point in the history
Closing the loop on
apollographql/federation#2958

- [ ] I'm unsure if override + log warning is the appropriate action, do
we have a similar pattern in config elsewhere?
- [x] Is there a place where the yaml config options are all documented?
I don't see anything existing for `reuse_query_fragments`.
- [ ] How to test the log warning was emitted?

---------

Co-authored-by: Bryn Cooke <BrynCooke@gmail.com>
  • Loading branch information
trevor-scheer and BrynCooke authored Apr 5, 2024
1 parent d951b41 commit f1d7c8a
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 6 deletions.
12 changes: 12 additions & 0 deletions .changesets/config_generate_query_fragments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
### Add `generate_query_fragments` configuration option ([PR #4885](https://github.com/apollographql/router/pull/4885))

Add a new `supergraph` configuration option `generate_query_fragments`. When set to `true`, the query planner will extract inline fragments into fragment definitions before sending queries to subgraphs. This can significantly reduce the size of the query sent to subgraphs, but may increase the time it takes to plan the query. Note that this option and `reuse_query_fragments` are mutually exclusive; if both are set to `true`, `generate_query_fragments` will take precedence.

An example router configuration:

```yaml title="router.yaml"
supergraph:
generate_query_fragments: true
```
By [@trevor-scheer](https://github.com/trevor-scheer) in https://github.com/apollographql/router/pull/4885
4 changes: 2 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5734,9 +5734,9 @@ dependencies = [

[[package]]
name = "router-bridge"
version = "0.5.16+v2.7.1"
version = "0.5.17+v2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224f69e11bdc5c16b582f82cd18647e305d31c9c20110635fcdf790c46405777"
checksum = "2f183e217179b38a4283e76ca62e3149ebe96512e9b1bd6b3933abab863f9a2c"
dependencies = [
"anyhow",
"async-channel 1.9.0",
Expand Down
2 changes: 1 addition & 1 deletion apollo-router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ reqwest = { version = "0.11.24", default-features = false, features = [
"stream",
] }
# note: this dependency should _always_ be pinned, prefix the version with an `=`
router-bridge = "=0.5.16+v2.7.1"
router-bridge = "=0.5.17+v2.7.2"
rust-embed = "8.2.0"
rustls = "0.21.10"
rustls-native-certs = "0.6.3"
Expand Down
28 changes: 26 additions & 2 deletions apollo-router/src/configuration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,10 @@ pub(crate) struct Supergraph {
#[serde(rename = "experimental_reuse_query_fragments")]
pub(crate) reuse_query_fragments: Option<bool>,

/// Enable QP generation of fragments for subgraph requests
/// Default: false
pub(crate) generate_query_fragments: bool,

/// Set to false to disable defer support
pub(crate) defer_support: bool,

Expand Down Expand Up @@ -641,6 +645,7 @@ impl Supergraph {
defer_support: Option<bool>,
query_planning: Option<QueryPlanning>,
reuse_query_fragments: Option<bool>,
generate_query_fragments: Option<bool>,
early_cancel: Option<bool>,
experimental_log_on_broken_pipe: Option<bool>,
) -> Self {
Expand All @@ -650,7 +655,16 @@ impl Supergraph {
introspection: introspection.unwrap_or_else(default_graphql_introspection),
defer_support: defer_support.unwrap_or_else(default_defer_support),
query_planning: query_planning.unwrap_or_default(),
reuse_query_fragments,
reuse_query_fragments: generate_query_fragments.and_then(|v|
if v {
if reuse_query_fragments.is_some_and(|v| v) {
// warn the user that both are enabled and it's overridden
tracing::warn!("Both 'generate_query_fragments' and 'experimental_reuse_query_fragments' are explicitly enabled, 'experimental_reuse_query_fragments' will be overridden to false");
}
Some(false)
} else { reuse_query_fragments }
),
generate_query_fragments: generate_query_fragments.unwrap_or_default(),
early_cancel: early_cancel.unwrap_or_default(),
experimental_log_on_broken_pipe: experimental_log_on_broken_pipe.unwrap_or_default(),
}
Expand All @@ -668,6 +682,7 @@ impl Supergraph {
defer_support: Option<bool>,
query_planning: Option<QueryPlanning>,
reuse_query_fragments: Option<bool>,
generate_query_fragments: Option<bool>,
early_cancel: Option<bool>,
experimental_log_on_broken_pipe: Option<bool>,
) -> Self {
Expand All @@ -677,7 +692,16 @@ impl Supergraph {
introspection: introspection.unwrap_or_else(default_graphql_introspection),
defer_support: defer_support.unwrap_or_else(default_defer_support),
query_planning: query_planning.unwrap_or_default(),
reuse_query_fragments,
reuse_query_fragments: generate_query_fragments.and_then(|v|
if v {
if reuse_query_fragments.is_some_and(|v| v) {
// warn the user that both are enabled and it's overridden
tracing::warn!("Both 'generate_query_fragments' and 'experimental_reuse_query_fragments' are explicitly enabled, 'experimental_reuse_query_fragments' will be overridden to false");
}
Some(false)
} else { reuse_query_fragments }
),
generate_query_fragments: generate_query_fragments.unwrap_or_default(),
early_cancel: early_cancel.unwrap_or_default(),
experimental_log_on_broken_pipe: experimental_log_on_broken_pipe.unwrap_or_default(),
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
source: apollo-router/src/configuration/tests.rs
assertion_line: 31
expression: "&schema"
---
{
Expand Down Expand Up @@ -2619,6 +2620,7 @@ expression: "&schema"
"path": "/",
"introspection": false,
"experimental_reuse_query_fragments": null,
"generate_query_fragments": false,
"defer_support": true,
"query_planning": {
"cache": {
Expand Down Expand Up @@ -2658,6 +2660,11 @@ expression: "&schema"
"type": "boolean",
"nullable": true
},
"generate_query_fragments": {
"description": "Enable QP generation of fragments for subgraph requests Default: false",
"default": false,
"type": "boolean"
},
"introspection": {
"description": "Enable introspection Default: false",
"default": false,
Expand Down
16 changes: 16 additions & 0 deletions apollo-router/src/configuration/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1000,3 +1000,19 @@ fn find_struct_name(lines: &[&str], line_number: usize) -> Option<String> {
})
.next()
}

#[test]
fn it_prevents_reuse_and_generate_query_fragments_simultaneously() {
let conf = Configuration::builder()
.supergraph(
Supergraph::builder()
.generate_query_fragments(true)
.reuse_query_fragments(true)
.build(),
)
.build()
.unwrap();

assert!(conf.supergraph.generate_query_fragments);
assert_eq!(conf.supergraph.reuse_query_fragments, Some(false));
}
1 change: 1 addition & 0 deletions apollo-router/src/introspection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ mod introspection_tests {
}),
graphql_validation: true,
reuse_query_fragments: Some(false),
generate_query_fragments: None,
debug: None,
},
)
Expand Down
4 changes: 4 additions & 0 deletions apollo-router/src/query_planner/bridge_query_planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ impl BridgeQueryPlanner {
sdl,
QueryPlannerConfig {
reuse_query_fragments: configuration.supergraph.reuse_query_fragments,
generate_query_fragments: Some(configuration.supergraph.generate_query_fragments),
incremental_delivery: Some(IncrementalDeliverySupport {
enable_defer: Some(configuration.supergraph.defer_support),
}),
Expand Down Expand Up @@ -289,6 +290,9 @@ impl BridgeQueryPlanner {
GraphQLValidationMode::Legacy | GraphQLValidationMode::Both
),
reuse_query_fragments: configuration.supergraph.reuse_query_fragments,
generate_query_fragments: Some(
configuration.supergraph.generate_query_fragments,
),
debug: Some(QueryPlannerDebugConfig {
bypass_planner_for_single_subgraph: None,
max_evaluated_plans: configuration
Expand Down
2 changes: 1 addition & 1 deletion apollo-router/tests/integration/redis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ mod test {
// 2. run `docker compose up -d` and connect to the redis container by running `docker exec -ti <container_id> /bin/bash`.
// 3. Run the `redis-cli` command from the shell and start the redis `monitor` command.
// 4. Run this test and yank the updated cache key from the redis logs.
let known_cache_key = "plan:v2.7.1:af1ee357bc75cfbbcc6adda41089a56e7d1d52f6d44c049739dde2c259314f58:2bf7810d3a47b31d8a77ebb09cdc784a3f77306827dc55b06770030a858167c7";
let known_cache_key = "plan:v2.7.2:af1ee357bc75cfbbcc6adda41089a56e7d1d52f6d44c049739dde2c259314f58:2bf7810d3a47b31d8a77ebb09cdc784a3f77306827dc55b06770030a858167c7";

let config = RedisConfig::from_url("redis://127.0.0.1:6379")?;
let client = RedisClient::new(config, None, None, None);
Expand Down
16 changes: 16 additions & 0 deletions docs/source/configuration/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,22 @@ example:
password: "${env.MY_PASSWORD}" #highlight-line
```

### Fragment reuse and generation

By default, the Apollo Router will attempt to reuse fragments from the original query while forming subgraph requests. This behavior can be disabled by setting the option to `false`:

```yaml
supergraph:
experimental_reuse_query_fragments: false
```

Alternatively, the Apollo Router can be configured to _generate_ fragments for subgraph requests. When set to `true`, the Apollo Router will extract _inline fragments only_ into fragment definitions before sending queries to subgraphs. This can significantly reduce the size of the query sent to subgraphs, but may increase the time it takes for planning. Note that this option and `experimental_reuse_query_fragments` are mutually exclusive; if both are explicitly set to `true`, `generate_query_fragments` will take precedence.

```yaml
supergraph:
generate_query_fragments: true
```

### Reusing configuration

You can reuse parts of your configuration file in multiple places using standard YAML aliasing syntax:
Expand Down

0 comments on commit f1d7c8a

Please sign in to comment.