diff --git a/.changesets/maint_tninesling_skip_disabled_dc_plugin.md b/.changesets/maint_tninesling_skip_disabled_dc_plugin.md new file mode 100644 index 0000000000..08709ea9f5 --- /dev/null +++ b/.changesets/maint_tninesling_skip_disabled_dc_plugin.md @@ -0,0 +1,5 @@ +### Reduce demand control allocations on start/reload ([PR #6754](https://github.com/apollographql/router/pull/6754)) + +When enabled, preallocates capacity for demand control's processed schema and shrinks to fit after processing. When disabled, skips the type processing entirely to minimize startup impact. + +By [@tninesling](https://github.com/tninesling) in https://github.com/apollographql/router/pull/6754 diff --git a/Cargo.lock b/Cargo.lock index 62d471d177..cedc725a1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3209,9 +3209,9 @@ dependencies = [ [[package]] name = "hickory-proto" -version = "0.24.1" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +checksum = "2ad3d6d98c648ed628df039541a5577bee1a7c83e9e16fe3dbedeea4cdfeb971" dependencies = [ "async-trait", "cfg-if", @@ -3220,7 +3220,7 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna 0.4.0", + "idna", "ipnet", "once_cell", "rand 0.8.5", @@ -3677,16 +3677,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "1.0.3" @@ -4050,7 +4040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -5222,7 +5212,7 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" dependencies = [ - "heck 0.5.0", + "heck 0.4.1", "itertools 0.13.0", "log", "multimap 0.10.0", @@ -7524,27 +7514,12 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-width" version = "0.1.14" @@ -7564,7 +7539,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 1.0.3", + "idna", "percent-encoding", "serde", ] diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index fa0a427ff1..0f75137286 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -11,6 +11,7 @@ license = "Elastic-2.0" rust-version = "1.83.0" edition = "2021" build = "build/main.rs" +default-run = "router" [[bin]] name = "router" diff --git a/apollo-router/src/compute_job.rs b/apollo-router/src/compute_job.rs index 64fe16b20d..a0bcab20a3 100644 --- a/apollo-router/src/compute_job.rs +++ b/apollo-router/src/compute_job.rs @@ -14,13 +14,21 @@ use crate::metrics::meter_provider; /// reaches `QUEUE_SOFT_CAPACITY_PER_THREAD * thread_pool_size()` const QUEUE_SOFT_CAPACITY_PER_THREAD: usize = 20; -/// Let this thread pool use all available resources if it can. +/// By default, let this thread pool use all available resources if it can. /// In the worst case, we’ll have moderate context switching cost /// as the kernel’s scheduler distributes time to it or Tokio or other threads. fn thread_pool_size() -> usize { - std::thread::available_parallelism() - .expect("available_parallelism() failed") - .get() + // This environment variable is intentionally undocumented. + if let Some(threads) = std::env::var("APOLLO_ROUTER_COMPUTE_THREADS") + .ok() + .and_then(|value| value.parse::().ok()) + { + threads + } else { + std::thread::available_parallelism() + .expect("available_parallelism() failed") + .get() + } } type Job = Box; diff --git a/apollo-router/src/executable.rs b/apollo-router/src/executable.rs index 84ffb393d6..b1445014f3 100644 --- a/apollo-router/src/executable.rs +++ b/apollo-router/src/executable.rs @@ -327,12 +327,16 @@ pub fn main() -> Result<()> { let mut builder = tokio::runtime::Builder::new_multi_thread(); builder.enable_all(); - if let Some(nb) = std::env::var("APOLLO_ROUTER_NUM_CORES") + + // This environment variable is intentionally undocumented. + // See also APOLLO_ROUTER_COMPUTE_THREADS in apollo-router/src/compute_job.rs + if let Some(nb) = std::env::var("APOLLO_ROUTER_IO_THREADS") .ok() .and_then(|value| value.parse::().ok()) { builder.worker_threads(nb); } + let runtime = builder.build()?; runtime.block_on(Executable::builder().start()) } diff --git a/apollo-router/src/plugins/demand_control/cost_calculator/schema.rs b/apollo-router/src/plugins/demand_control/cost_calculator/schema.rs index b5e620609a..6f8a6cf259 100644 --- a/apollo-router/src/plugins/demand_control/cost_calculator/schema.rs +++ b/apollo-router/src/plugins/demand_control/cost_calculator/schema.rs @@ -156,16 +156,16 @@ impl DemandControlledSchema { pub(crate) fn new(schema: Arc>) -> Result { let fed_schema = ValidFederationSchema::new((*schema).clone())?; let mut input_field_definitions: HashMap> = - HashMap::new(); + HashMap::with_capacity(schema.types.len()); let mut output_field_definitions: HashMap> = - HashMap::new(); + HashMap::with_capacity(schema.types.len()); for (type_name, type_) in &schema.types { match type_ { ExtendedType::Interface(ty) => { let type_fields = output_field_definitions .entry(type_name.clone()) - .or_default(); + .or_insert_with(|| HashMap::with_capacity(ty.fields.len())); for (field_name, field_definition) in &ty.fields { type_fields.insert( field_name.clone(), @@ -176,7 +176,7 @@ impl DemandControlledSchema { ExtendedType::Object(ty) => { let type_fields = output_field_definitions .entry(type_name.clone()) - .or_default(); + .or_insert_with(|| HashMap::with_capacity(ty.fields.len())); for (field_name, field_definition) in &ty.fields { type_fields.insert( field_name.clone(), @@ -187,7 +187,7 @@ impl DemandControlledSchema { ExtendedType::InputObject(ty) => { let type_fields = input_field_definitions .entry(type_name.clone()) - .or_default(); + .or_insert_with(|| HashMap::with_capacity(ty.fields.len())); for (field_name, field_definition) in &ty.fields { type_fields.insert( field_name.clone(), @@ -201,6 +201,9 @@ impl DemandControlledSchema { } } + input_field_definitions.shrink_to_fit(); + output_field_definitions.shrink_to_fit(); + Ok(Self { inner: fed_schema, input_field_definitions, @@ -208,6 +211,15 @@ impl DemandControlledSchema { }) } + pub(crate) fn empty(schema: Arc>) -> Result { + let fed_schema = ValidFederationSchema::new((*schema).clone())?; + Ok(Self { + inner: fed_schema, + input_field_definitions: Default::default(), + output_field_definitions: Default::default(), + }) + } + pub(in crate::plugins::demand_control) fn input_field_definition( &self, type_name: &str, diff --git a/apollo-router/src/plugins/demand_control/mod.rs b/apollo-router/src/plugins/demand_control/mod.rs index 6a3c506f1f..a151e1ded2 100644 --- a/apollo-router/src/plugins/demand_control/mod.rs +++ b/apollo-router/src/plugins/demand_control/mod.rs @@ -306,6 +306,19 @@ impl Plugin for DemandControl { type Config = DemandControlConfig; async fn new(init: PluginInit) -> Result { + if !init.config.enabled { + return Ok(DemandControl { + strategy_factory: StrategyFactory::new( + init.config.clone(), + Arc::new(DemandControlledSchema::empty( + init.supergraph_schema.clone(), + )?), + Arc::new(HashMap::new()), + ), + config: init.config, + }); + } + let demand_controlled_supergraph_schema = DemandControlledSchema::new(init.supergraph_schema.clone())?; let mut demand_controlled_subgraph_schemas = HashMap::new(); diff --git a/apollo-router/src/services/layers/persisted_queries/snapshots/apollo_router__services__layers__persisted_queries__tests__pq_layer_freeform_graphql_with_safelist_log_unknown_true@logs.snap b/apollo-router/src/services/layers/persisted_queries/snapshots/apollo_router__services__layers__persisted_queries__tests__pq_layer_freeform_graphql_with_safelist_log_unknown_true@logs.snap index f9a850f1c8..d8c2c9b330 100644 --- a/apollo-router/src/services/layers/persisted_queries/snapshots/apollo_router__services__layers__persisted_queries__tests__pq_layer_freeform_graphql_with_safelist_log_unknown_true@logs.snap +++ b/apollo-router/src/services/layers/persisted_queries/snapshots/apollo_router__services__layers__persisted_queries__tests__pq_layer_freeform_graphql_with_safelist_log_unknown_true@logs.snap @@ -1,7 +1,6 @@ --- source: apollo-router/src/services/layers/persisted_queries/mod.rs expression: yaml -snapshot_kind: text --- - fields: operation_body: "query SomeQuery { me { id } }"