From c7a7f7e8f274d80242b4f1a5110713a7c254065d Mon Sep 17 00:00:00 2001 From: Bryn Cooke Date: Tue, 29 Aug 2023 16:20:09 +0100 Subject: [PATCH] Uplink connections now reuse reqwest client (#3703) Previously uplink requests created a new reqwest client each time, this may cause CPU spikes especially on OSX. Fixes #3333 **Checklist** Complete the checklist (and note appropriate exceptions) before a final PR is raised. - [ ] Changes are compatible[^1] - [ ] Documentation[^2] completed - [ ] Performance impact assessed and acceptable - Tests added and passing[^3] - [ ] Unit Tests - [ ] Integration Tests - [ ] Manual Tests **Exceptions** *Note any exceptions here* **Notes** [^1]. It may be appropriate to bring upcoming changes to the attention of other (impacted) groups. Please endeavour to do this before seeking PR approval. The mechanism for doing this will vary considerably, so use your judgement as to how and when to do this. [^2]. Configuration is an important part of many changes. Where applicable please try to document configuration examples. [^3]. Tick whichever testing boxes are applicable. If you are adding Manual Tests: - please document the manual testing (extensively) in the Exceptions. - please raise a separate issue to automate the test and label it (or ask for it to be labeled) as `manual test` Co-authored-by: bryn --- .changesets/maint_bryn_uplink_client.md | 6 +++++ apollo-router/src/uplink/mod.rs | 29 +++++++++++++++---------- 2 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 .changesets/maint_bryn_uplink_client.md diff --git a/.changesets/maint_bryn_uplink_client.md b/.changesets/maint_bryn_uplink_client.md new file mode 100644 index 0000000000..7c9776f106 --- /dev/null +++ b/.changesets/maint_bryn_uplink_client.md @@ -0,0 +1,6 @@ +### Uplink connections now reuse reqwest client ([Issue #3333](https://github.com/apollographql/router/issues/3333)) + +Previously uplink requests created a new reqwest client each time, this may cause CPU spikes especially on OSX. +A single client will now be shared between requests of the same type. + +By [@BrynCooke](https://github.com/BrynCooke) in https://github.com/apollographql/router/pull/3703 \ No newline at end of file diff --git a/apollo-router/src/uplink/mod.rs b/apollo-router/src/uplink/mod.rs index 8f10a44c28..b72ef91935 100644 --- a/apollo-router/src/uplink/mod.rs +++ b/apollo-router/src/uplink/mod.rs @@ -4,6 +4,7 @@ use std::time::Duration; use std::time::Instant; use futures::Stream; +use futures::StreamExt; use graphql_client::QueryBody; use thiserror::Error; use tokio::sync::mpsc::channel; @@ -169,6 +170,17 @@ where { let query = query_name::(); let (sender, receiver) = channel(2); + let client = match reqwest::Client::builder() + .timeout(uplink_config.timeout) + .build() + { + Ok(client) => client, + Err(err) => { + tracing::error!("unable to create client to query uplink: {err}", err = err); + return futures::stream::empty().boxed(); + } + }; + let task = async move { let mut last_id = None; let mut endpoints = uplink_config.endpoints.unwrap_or_default(); @@ -181,13 +193,7 @@ where let query_body = Query::build_query(variables.into()); - match fetch::( - &query_body, - &mut endpoints.iter(), - uplink_config.timeout, - ) - .await - { + match fetch::(&client, &query_body, &mut endpoints.iter()).await { Ok(response) => { tracing::info!( counter.apollo_router_uplink_fetch_count_total = 1, @@ -255,13 +261,13 @@ where }; drop(tokio::task::spawn(task.with_current_subscriber())); - ReceiverStream::new(receiver) + ReceiverStream::new(receiver).boxed() } pub(crate) async fn fetch( + client: &reqwest::Client, request_body: &QueryBody, urls: &mut impl Iterator, - timeout: Duration, ) -> Result, Error> where Query: graphql_client::GraphQLQuery, @@ -272,7 +278,7 @@ where let query = query_name::(); for url in urls { let now = Instant::now(); - match http_request::(url.as_str(), request_body, timeout).await { + match http_request::(client, url.as_str(), request_body).await { Ok(response) => { let response = response.data.map(Into::into); match &response { @@ -352,14 +358,13 @@ fn query_name() -> &'static str { } async fn http_request( + client: &reqwest::Client, url: &str, request_body: &QueryBody, - timeout: Duration, ) -> Result, reqwest::Error> where Query: graphql_client::GraphQLQuery, { - let client = reqwest::Client::builder().timeout(timeout).build()?; // It is possible that istio-proxy is re-configuring networking beneath us. If it is, we'll see an error something like this: // level: "ERROR" // message: "fetch failed from all endpoints"