From b35b4bf7d70a162c4c9a04ff3564c3ccad5c27b2 Mon Sep 17 00:00:00 2001 From: Shashi Kant Date: Mon, 6 May 2024 09:51:24 +0000 Subject: [PATCH] using Arc to optimize Blueprint cloning --- ...impl_path_string_for_evaluation_context.rs | 8 +++++--- src/app_context.rs | 4 ++-- src/blueprint/blueprint.rs | 8 ++++---- src/blueprint/from_config.rs | 19 ++++++++++++------- src/blueprint/telemetry.rs | 2 +- src/cli/runtime/mod.rs | 2 +- src/cli/server/server_config.rs | 4 ++-- src/cli/telemetry.rs | 3 ++- src/http/request_context.rs | 14 +++++++++----- src/http/request_handler.rs | 2 +- src/path.rs | 4 +++- 11 files changed, 42 insertions(+), 28 deletions(-) diff --git a/benches/impl_path_string_for_evaluation_context.rs b/benches/impl_path_string_for_evaluation_context.rs index fbd622f03e..d8c7c534f3 100644 --- a/benches/impl_path_string_for_evaluation_context.rs +++ b/benches/impl_path_string_for_evaluation_context.rs @@ -247,14 +247,16 @@ fn request_context() -> RequestContext { extensions: Arc::new(vec![]), }; RequestContext::new(runtime) - .server(server) - .upstream(upstream) + .server(Arc::new(server)) + .upstream(Arc::new(upstream)) } pub fn bench_main(c: &mut Criterion) { let mut req_ctx = request_context().allowed_headers(TEST_HEADERS.clone()); - req_ctx.server.vars = TEST_VARS.clone(); + let mut server = req_ctx.server.as_ref().clone(); + server.vars = TEST_VARS.clone(); + req_ctx.server = Arc::new(server); let eval_ctx = EvaluationContext::new(&req_ctx, &MockGraphqlContext); assert_test(&eval_ctx); diff --git a/src/app_context.rs b/src/app_context.rs index 82bca0b3de..6446083fb9 100644 --- a/src/app_context.rs +++ b/src/app_context.rs @@ -18,7 +18,7 @@ use crate::runtime::TargetRuntime; pub struct AppContext { pub schema: dynamic::Schema, pub runtime: TargetRuntime, - pub blueprint: Blueprint, + pub blueprint: Arc, pub http_data_loaders: Arc>>, pub gql_data_loaders: Arc>>, pub grpc_data_loaders: Arc>>, @@ -117,7 +117,7 @@ impl AppContext { AppContext { schema, runtime, - blueprint, + blueprint: Arc::new(blueprint), http_data_loaders: Arc::new(http_data_loaders), gql_data_loaders: Arc::new(gql_data_loaders), grpc_data_loaders: Arc::new(grpc_data_loaders), diff --git a/src/blueprint/blueprint.rs b/src/blueprint/blueprint.rs index e9289faeb9..13db2b1a5f 100644 --- a/src/blueprint/blueprint.rs +++ b/src/blueprint/blueprint.rs @@ -21,10 +21,10 @@ use crate::schema_extension::SchemaExtension; #[derive(Clone, Debug, Default, Setters)] pub struct Blueprint { pub definitions: Vec, - pub schema: SchemaDefinition, - pub server: Server, - pub upstream: Upstream, - pub telemetry: Telemetry, + pub schema: Arc, + pub server: Arc, + pub upstream: Arc, + pub telemetry: Arc, } #[derive(Clone, Debug)] diff --git a/src/blueprint/from_config.rs b/src/blueprint/from_config.rs index f786a56802..f437bfab82 100644 --- a/src/blueprint/from_config.rs +++ b/src/blueprint/from_config.rs @@ -1,4 +1,5 @@ use std::collections::{BTreeMap, HashMap}; +use std::sync::Arc; use async_graphql::dynamic::SchemaBuilder; @@ -14,12 +15,13 @@ use crate::valid::{Valid, ValidationError, Validator}; pub fn config_blueprint<'a>() -> TryFold<'a, ConfigModule, Blueprint, String> { let server = TryFoldConfig::::new(|config_module, blueprint| { - Valid::from(Server::try_from(config_module.clone())).map(|server| blueprint.server(server)) + Valid::from(Server::try_from(config_module.clone())) + .map(|server| blueprint.server(Arc::new(server))) }); let schema = to_schema().transform::( - |schema, blueprint| blueprint.schema(schema), - |blueprint| blueprint.schema, + |schema, blueprint| blueprint.schema(Arc::new(schema)), + |blueprint| blueprint.schema.as_ref().clone(), ); let definitions = to_definitions().transform::( @@ -28,7 +30,8 @@ pub fn config_blueprint<'a>() -> TryFold<'a, ConfigModule, Blueprint, String> { ); let upstream = TryFoldConfig::::new(|config_module, blueprint| { - Valid::from(Upstream::try_from(config_module)).map(|upstream| blueprint.upstream(upstream)) + Valid::from(Upstream::try_from(config_module)) + .map(|upstream| blueprint.upstream(Arc::new(upstream))) }); let links = TryFoldConfig::::new(|config_module, blueprint| { @@ -36,8 +39,8 @@ pub fn config_blueprint<'a>() -> TryFold<'a, ConfigModule, Blueprint, String> { }); let opentelemetry = to_opentelemetry().transform::( - |opentelemetry, blueprint| blueprint.telemetry(opentelemetry), - |blueprint| blueprint.telemetry, + |opentelemetry, blueprint| blueprint.telemetry(Arc::new(opentelemetry)), + |blueprint| blueprint.telemetry.as_ref().clone(), ); server @@ -59,7 +62,9 @@ pub fn apply_batching(mut blueprint: Blueprint) -> Blueprint { if let Some(Expression::IO(IO::Http { group_by: Some(_), .. })) = field.resolver.clone() { - blueprint.upstream.batch = blueprint.upstream.batch.or(Some(Batch::default())); + let mut upstream = blueprint.upstream.as_ref().clone(); + upstream.batch = upstream.batch.or(Some(Batch::default())); + blueprint.upstream = Arc::new(upstream); return blueprint; } } diff --git a/src/blueprint/telemetry.rs b/src/blueprint/telemetry.rs index 1df93ad7e4..0a05928768 100644 --- a/src/blueprint/telemetry.rs +++ b/src/blueprint/telemetry.rs @@ -24,7 +24,7 @@ pub enum TelemetryExporter { Apollo(Apollo), } -#[derive(Debug, Default, Clone)] +#[derive(Clone, Debug, Default)] pub struct Telemetry { pub export: Option, pub request_headers: Vec, diff --git a/src/cli/runtime/mod.rs b/src/cli/runtime/mod.rs index b0499c6f35..72c9262e81 100644 --- a/src/cli/runtime/mod.rs +++ b/src/cli/runtime/mod.rs @@ -42,7 +42,7 @@ fn init_http(blueprint: &Blueprint) -> Arc { // Provides access to http in native rust environment fn init_http2_only(blueprint: &Blueprint) -> Arc { let http_io = http::NativeHttp::init( - &blueprint.upstream.clone().http2_only(true), + &blueprint.upstream.as_ref().clone().http2_only(true), &blueprint.telemetry, ); init_hook_http(Arc::new(http_io), blueprint.server.script.clone()) diff --git a/src/cli/server/server_config.rs b/src/cli/server/server_config.rs index e1b60bc0e1..64745561e6 100644 --- a/src/cli/server/server_config.rs +++ b/src/cli/server/server_config.rs @@ -11,7 +11,7 @@ use crate::rest::{EndpointSet, Unchecked}; use crate::schema_extension::SchemaExtension; pub struct ServerConfig { - pub blueprint: Blueprint, + pub blueprint: Arc, pub app_ctx: Arc, } @@ -39,7 +39,7 @@ impl ServerConfig { let endpoints = endpoints.into_checked(&blueprint, rt.clone()).await?; let app_context = Arc::new(AppContext::new(blueprint.clone(), rt, endpoints)); - Ok(Self { app_ctx: app_context, blueprint }) + Ok(Self { app_ctx: app_context, blueprint: Arc::new(blueprint) }) } pub fn addr(&self) -> SocketAddr { diff --git a/src/cli/telemetry.rs b/src/cli/telemetry.rs index 8b5b7f574f..2741c8a714 100644 --- a/src/cli/telemetry.rs +++ b/src/cli/telemetry.rs @@ -1,4 +1,5 @@ use std::io::Write; +use std::sync::Arc; use anyhow::{anyhow, Result}; use once_cell::sync::Lazy; @@ -193,7 +194,7 @@ fn set_tracing_subscriber(subscriber: impl Subscriber + Send + Sync) { let _ = tracing::subscriber::set_global_default(subscriber); } -pub fn init_opentelemetry(config: Telemetry, runtime: &TargetRuntime) -> anyhow::Result<()> { +pub fn init_opentelemetry(config: Arc, runtime: &TargetRuntime) -> anyhow::Result<()> { if let Some(export) = &config.export { global::set_error_handler(|error| { if !matches!( diff --git a/src/http/request_context.rs b/src/http/request_context.rs index 7a3e8da519..251a6ff402 100644 --- a/src/http/request_context.rs +++ b/src/http/request_context.rs @@ -20,8 +20,8 @@ use crate::runtime::TargetRuntime; #[derive(Setters)] pub struct RequestContext { - pub server: Server, - pub upstream: Upstream, + pub server: Arc, + pub upstream: Arc, pub x_response_headers: Arc>, pub cookie_headers: Option>>, // A subset of all the headers received in the GraphQL Request that will be sent to the @@ -207,6 +207,8 @@ impl From<&AppContext> for RequestContext { #[cfg(test)] mod test { + use std::sync::Arc; + use cache_control::Cachability; use crate::blueprint::{Server, Upstream}; @@ -220,8 +222,8 @@ mod test { let upstream = Upstream::try_from(&config_module).unwrap(); let server = Server::try_from(config_module).unwrap(); RequestContext::new(crate::runtime::test::init(None)) - .upstream(upstream) - .server(server) + .upstream(Arc::new(upstream)) + .server(Arc::new(server)) } } @@ -269,7 +271,9 @@ mod test { let mut upstream = Upstream::try_from(&config_module).unwrap(); let server = Server::try_from(config_module).unwrap(); upstream.batch = Some(Batch::default()); - let req_ctx: RequestContext = RequestContext::default().upstream(upstream).server(server); + let req_ctx: RequestContext = RequestContext::default() + .upstream(Arc::new(upstream)) + .server(Arc::new(server)); assert!(req_ctx.is_batching_enabled()); } diff --git a/src/http/request_handler.rs b/src/http/request_handler.rs index c7a0e1a719..e954e333cc 100644 --- a/src/http/request_handler.rs +++ b/src/http/request_handler.rs @@ -52,7 +52,7 @@ fn not_found() -> Result> { } fn create_request_context(req: &Request, app_ctx: &AppContext) -> RequestContext { - let upstream = app_ctx.blueprint.upstream.clone(); + let upstream = app_ctx.blueprint.upstream.as_ref().clone(); let allowed = upstream.allowed_headers; let allowed_headers = create_allowed_headers(req.headers(), &allowed); diff --git a/src/path.rs b/src/path.rs index f80ff9a0be..e2487d1470 100644 --- a/src/path.rs +++ b/src/path.rs @@ -211,7 +211,9 @@ mod tests { static REQ_CTX: Lazy = Lazy::new(|| { let mut req_ctx = RequestContext::default().allowed_headers(TEST_HEADERS.clone()); - req_ctx.server.vars = TEST_VARS.clone(); + let mut server = req_ctx.server.as_ref().clone(); + server.vars = TEST_VARS.clone(); + req_ctx.server = Arc::new(server); req_ctx.runtime.env = Arc::new(Env::init(TEST_ENV_VARS.clone())); req_ctx