From a2b0ca961e29297ab3e0d542b93e7d7651b394a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Wed, 5 Jun 2024 10:39:52 +0200 Subject: [PATCH 1/3] fix(compiler): cache the implementers map per operation Fixes #862. I had proposed a different approach that would let us cache the implementers map for as long as the schema is immutable, so it could be reused for different query validations. That approach had an issue that made `Valid::assume_valid_ref` very, very subtle to use, so we are not doing that right now. This is a less ideal version but it does solve the immediate problem. We already pass around an `OperationValidationConfig` structure and this seems like a nice, non-invasive place to put the implementers cache. --- .../src/executable/validation.rs | 5 +-- .../apollo-compiler/src/validation/field.rs | 2 +- .../src/validation/fragment.rs | 11 +++-- .../src/validation/operation.rs | 42 +++++++++++++++---- .../src/validation/selection.rs | 16 +++---- 5 files changed, 49 insertions(+), 27 deletions(-) diff --git a/crates/apollo-compiler/src/executable/validation.rs b/crates/apollo-compiler/src/executable/validation.rs index 409307a72..f4df78bfb 100644 --- a/crates/apollo-compiler/src/executable/validation.rs +++ b/crates/apollo-compiler/src/executable/validation.rs @@ -57,9 +57,6 @@ pub(crate) fn validate_field_set( document, Some((schema, &field_set.selection_set.ty)), &field_set.selection_set, - crate::validation::operation::OperationValidationConfig { - schema: Some(schema), - variables: &[], - }, + &crate::validation::operation::OperationValidationConfig::new(Some(schema), &[]), ) } diff --git a/crates/apollo-compiler/src/validation/field.rs b/crates/apollo-compiler/src/validation/field.rs index c1a3e883d..64c07b2e2 100644 --- a/crates/apollo-compiler/src/validation/field.rs +++ b/crates/apollo-compiler/src/validation/field.rs @@ -14,7 +14,7 @@ pub(crate) fn validate_field( // May be None if a parent selection was invalid against_type: Option<(&crate::Schema, &ast::NamedType)>, field: &Node, - context: OperationValidationConfig<'_>, + context: &OperationValidationConfig<'_>, ) { // First do all the validation that we can without knowing the type of the field. diff --git a/crates/apollo-compiler/src/validation/fragment.rs b/crates/apollo-compiler/src/validation/fragment.rs index 093566f47..f5c0aa8a5 100644 --- a/crates/apollo-compiler/src/validation/fragment.rs +++ b/crates/apollo-compiler/src/validation/fragment.rs @@ -52,6 +52,7 @@ fn validate_fragment_spread_type( against_type: &NamedType, type_condition: &NamedType, selection: &executable::Selection, + context: &OperationValidationConfig<'_>, ) { // Another diagnostic will be raised if the type condition was wrong. // We reduce noise by silencing other issues with the fragment. @@ -64,7 +65,7 @@ fn validate_fragment_spread_type( return; }; - let implementers_map = schema.implementers_map(); + let implementers_map = context.implementers_map(); let concrete_parent_types = get_possible_types(against_type_definition, &implementers_map); let concrete_condition_types = get_possible_types(type_condition_definition, &implementers_map); @@ -107,7 +108,7 @@ pub(crate) fn validate_inline_fragment( document: &ExecutableDocument, against_type: Option<(&crate::Schema, &ast::NamedType)>, inline: &Node, - context: OperationValidationConfig<'_>, + context: &OperationValidationConfig<'_>, ) { super::directive::validate_directives( diagnostics, @@ -138,6 +139,7 @@ pub(crate) fn validate_inline_fragment( against_type, type_condition, &executable::Selection::InlineFragment(inline.clone()), + &context, ); } super::selection::validate_selection_set( @@ -159,7 +161,7 @@ pub(crate) fn validate_fragment_spread( document: &ExecutableDocument, against_type: Option<(&crate::Schema, &NamedType)>, spread: &Node, - context: OperationValidationConfig<'_>, + context: &OperationValidationConfig<'_>, ) { super::directive::validate_directives( diagnostics, @@ -179,6 +181,7 @@ pub(crate) fn validate_fragment_spread( against_type, def.type_condition(), &executable::Selection::FragmentSpread(spread.clone()), + context, ); } validate_fragment_definition(diagnostics, document, def, context); @@ -198,7 +201,7 @@ pub(crate) fn validate_fragment_definition( diagnostics: &mut DiagnosticList, document: &ExecutableDocument, fragment: &Node, - context: OperationValidationConfig<'_>, + context: &OperationValidationConfig<'_>, ) { super::directive::validate_directives( diagnostics, diff --git a/crates/apollo-compiler/src/validation/operation.rs b/crates/apollo-compiler/src/validation/operation.rs index a5f9c5975..484369441 100644 --- a/crates/apollo-compiler/src/validation/operation.rs +++ b/crates/apollo-compiler/src/validation/operation.rs @@ -1,12 +1,41 @@ +use std::collections::HashMap; +use std::sync::OnceLock; + +use crate::ast; +use crate::ast::Name; +use crate::executable; +use crate::schema::Implementers; use crate::validation::DiagnosticList; -use crate::{ast, executable, ExecutableDocument, Node, Schema}; +use crate::ExecutableDocument; +use crate::Node; +use crate::Schema; -#[derive(Debug, Clone)] +#[derive(Debug)] pub(crate) struct OperationValidationConfig<'a> { /// When None, rules that require a schema to validate are disabled. - pub schema: Option<&'a crate::Schema>, + pub schema: Option<&'a Schema>, /// The variables defined for this operation. pub variables: &'a [Node], + implementers_map: OnceLock>, +} + +impl<'a> OperationValidationConfig<'a> { + pub fn new(schema: Option<&'a Schema>, variables: &'a [Node]) -> Self { + Self { + schema, + variables, + implementers_map: Default::default(), + } + } + + /// Returns a cached reference to the implementers map. + pub fn implementers_map(&self) -> &HashMap { + self.implementers_map.get_or_init(|| { + self.schema + .map(|schema| schema.implementers_map()) + .unwrap_or_default() + }) + } } pub(crate) fn validate_subscription( @@ -60,10 +89,7 @@ pub(crate) fn validate_operation( document: &ExecutableDocument, operation: &executable::Operation, ) { - let config = OperationValidationConfig { - schema, - variables: &operation.variables, - }; + let config = OperationValidationConfig::new(schema, &operation.variables); let against_type = if let Some(schema) = schema { schema @@ -92,7 +118,7 @@ pub(crate) fn validate_operation( document, against_type, &operation.selection_set, - config, + &config, ); } diff --git a/crates/apollo-compiler/src/validation/selection.rs b/crates/apollo-compiler/src/validation/selection.rs index 514e9c616..8741b02d5 100644 --- a/crates/apollo-compiler/src/validation/selection.rs +++ b/crates/apollo-compiler/src/validation/selection.rs @@ -545,24 +545,20 @@ pub(crate) fn validate_selection_set( document: &ExecutableDocument, against_type: Option<(&crate::Schema, &NamedType)>, selection_set: &SelectionSet, - context: OperationValidationConfig<'_>, + context: &OperationValidationConfig<'_>, ) { for selection in &selection_set.selections { match selection { - executable::Selection::Field(field) => super::field::validate_field( - diagnostics, - document, - against_type, - field, - context.clone(), - ), + executable::Selection::Field(field) => { + super::field::validate_field(diagnostics, document, against_type, field, context) + } executable::Selection::FragmentSpread(fragment) => { super::fragment::validate_fragment_spread( diagnostics, document, against_type, fragment, - context.clone(), + context, ) } executable::Selection::InlineFragment(inline) => { @@ -571,7 +567,7 @@ pub(crate) fn validate_selection_set( document, against_type, inline, - context.clone(), + context, ) } } From ad30e71d85e41274968b98ebbc3abdedfd044ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Wed, 5 Jun 2024 11:08:48 +0200 Subject: [PATCH 2/3] fix(compiler): share the cached implementers map between different operations in the same document --- .../src/executable/validation.rs | 7 +- .../apollo-compiler/src/validation/field.rs | 6 +- .../src/validation/fragment.rs | 30 ++++---- crates/apollo-compiler/src/validation/mod.rs | 72 ++++++++++++++++++- .../src/validation/operation.rs | 52 +++----------- .../src/validation/selection.rs | 4 +- 6 files changed, 103 insertions(+), 68 deletions(-) diff --git a/crates/apollo-compiler/src/executable/validation.rs b/crates/apollo-compiler/src/executable/validation.rs index f4df78bfb..9d701154e 100644 --- a/crates/apollo-compiler/src/executable/validation.rs +++ b/crates/apollo-compiler/src/executable/validation.rs @@ -3,6 +3,7 @@ use crate::validation::fragment::validate_fragment_used; use crate::validation::operation::validate_operation_definitions; use crate::validation::selection::FieldsInSetCanMerge; use crate::validation::DiagnosticList; +use crate::validation::ExecutableValidationContext; use crate::validation::Valid; use crate::ExecutableDocument; use crate::Schema; @@ -40,7 +41,8 @@ pub(crate) fn validate_with_or_without_schema( schema: Option<&Schema>, document: &ExecutableDocument, ) { - validate_operation_definitions(errors, schema, document); + let context = ExecutableValidationContext::new(schema); + validate_operation_definitions(errors, document, &context); for def in document.fragments.values() { validate_fragment_used(errors, document, def); } @@ -52,11 +54,12 @@ pub(crate) fn validate_field_set( field_set: &FieldSet, ) { let document = &ExecutableDocument::new(); // No fragment definitions + let context = ExecutableValidationContext::new(Some(schema)); crate::validation::selection::validate_selection_set( diagnostics, document, Some((schema, &field_set.selection_set.ty)), &field_set.selection_set, - &crate::validation::operation::OperationValidationConfig::new(Some(schema), &[]), + context.operation_context(&[]), ) } diff --git a/crates/apollo-compiler/src/validation/field.rs b/crates/apollo-compiler/src/validation/field.rs index 64c07b2e2..f9721aaa0 100644 --- a/crates/apollo-compiler/src/validation/field.rs +++ b/crates/apollo-compiler/src/validation/field.rs @@ -2,10 +2,10 @@ use crate::coordinate::{FieldArgumentCoordinate, TypeAttributeCoordinate}; use crate::validation::diagnostics::DiagnosticData; use crate::{ast, executable, schema, ExecutableDocument, Node}; -use super::operation::OperationValidationConfig; use crate::ast::Name; use crate::schema::Component; use crate::validation::DiagnosticList; +use crate::validation::OperationValidationContext; use indexmap::IndexMap; pub(crate) fn validate_field( @@ -14,13 +14,13 @@ pub(crate) fn validate_field( // May be None if a parent selection was invalid against_type: Option<(&crate::Schema, &ast::NamedType)>, field: &Node, - context: &OperationValidationConfig<'_>, + context: OperationValidationContext<'_>, ) { // First do all the validation that we can without knowing the type of the field. super::directive::validate_directives( diagnostics, - context.schema, + context.schema(), field.directives.iter(), ast::DirectiveLocation::Field, context.variables, diff --git a/crates/apollo-compiler/src/validation/fragment.rs b/crates/apollo-compiler/src/validation/fragment.rs index f5c0aa8a5..8be3ec3eb 100644 --- a/crates/apollo-compiler/src/validation/fragment.rs +++ b/crates/apollo-compiler/src/validation/fragment.rs @@ -5,8 +5,8 @@ use crate::executable; use crate::schema; use crate::schema::Implementers; use crate::validation::diagnostics::DiagnosticData; -use crate::validation::operation::OperationValidationConfig; use crate::validation::DiagnosticList; +use crate::validation::OperationValidationContext; use crate::validation::{CycleError, NodeLocation, RecursionGuard, RecursionStack}; use crate::ExecutableDocument; use crate::Node; @@ -52,7 +52,7 @@ fn validate_fragment_spread_type( against_type: &NamedType, type_condition: &NamedType, selection: &executable::Selection, - context: &OperationValidationConfig<'_>, + context: OperationValidationContext<'_>, ) { // Another diagnostic will be raised if the type condition was wrong. // We reduce noise by silencing other issues with the fragment. @@ -66,8 +66,8 @@ fn validate_fragment_spread_type( }; let implementers_map = context.implementers_map(); - let concrete_parent_types = get_possible_types(against_type_definition, &implementers_map); - let concrete_condition_types = get_possible_types(type_condition_definition, &implementers_map); + let concrete_parent_types = get_possible_types(against_type_definition, implementers_map); + let concrete_condition_types = get_possible_types(type_condition_definition, implementers_map); let mut applicable_types = concrete_parent_types.intersection(&concrete_condition_types); if applicable_types.next().is_none() { @@ -108,18 +108,18 @@ pub(crate) fn validate_inline_fragment( document: &ExecutableDocument, against_type: Option<(&crate::Schema, &ast::NamedType)>, inline: &Node, - context: &OperationValidationConfig<'_>, + context: OperationValidationContext<'_>, ) { super::directive::validate_directives( diagnostics, - context.schema, + context.schema(), inline.directives.iter(), ast::DirectiveLocation::InlineFragment, context.variables, ); let previous = diagnostics.len(); - if let Some(schema) = context.schema { + if let Some(schema) = context.schema() { if let Some(t) = &inline.type_condition { validate_fragment_type_condition(diagnostics, schema, None, t, inline.location()) } @@ -139,13 +139,13 @@ pub(crate) fn validate_inline_fragment( against_type, type_condition, &executable::Selection::InlineFragment(inline.clone()), - &context, + context, ); } super::selection::validate_selection_set( diagnostics, document, - if let (Some(schema), Some(ty)) = (&context.schema, &inline.type_condition) { + if let (Some(schema), Some(ty)) = (&context.schema(), &inline.type_condition) { Some((schema, ty)) } else { against_type @@ -161,11 +161,11 @@ pub(crate) fn validate_fragment_spread( document: &ExecutableDocument, against_type: Option<(&crate::Schema, &NamedType)>, spread: &Node, - context: &OperationValidationConfig<'_>, + context: OperationValidationContext<'_>, ) { super::directive::validate_directives( diagnostics, - context.schema, + context.schema(), spread.directives.iter(), ast::DirectiveLocation::FragmentSpread, context.variables, @@ -201,18 +201,18 @@ pub(crate) fn validate_fragment_definition( diagnostics: &mut DiagnosticList, document: &ExecutableDocument, fragment: &Node, - context: &OperationValidationConfig<'_>, + context: OperationValidationContext<'_>, ) { super::directive::validate_directives( diagnostics, - context.schema, + context.schema(), fragment.directives.iter(), ast::DirectiveLocation::FragmentDefinition, context.variables, ); let previous = diagnostics.len(); - if let Some(schema) = context.schema { + if let Some(schema) = context.schema() { validate_fragment_type_condition( diagnostics, schema, @@ -231,7 +231,7 @@ pub(crate) fn validate_fragment_definition( // If the type does not exist, do not attempt to validate the selections against it; // it has either already raised an error, or we are validating an executable without // a schema. - let type_condition = context.schema.and_then(|schema| { + let type_condition = context.schema().and_then(|schema| { schema .types .contains_key(fragment.type_condition()) diff --git a/crates/apollo-compiler/src/validation/mod.rs b/crates/apollo-compiler/src/validation/mod.rs index fb1fa6cee..136ef319e 100644 --- a/crates/apollo-compiler/src/validation/mod.rs +++ b/crates/apollo-compiler/src/validation/mod.rs @@ -3,7 +3,8 @@ use crate::coordinate::SchemaCoordinate; #[cfg(doc)] -use crate::{ExecutableDocument, Schema}; +use crate::ExecutableDocument; +use crate::Schema; pub(crate) mod argument; pub(crate) mod diagnostics; @@ -25,13 +26,16 @@ pub(crate) mod variable; use crate::ast::Name; use crate::diagnostic::{CliReport, Diagnostic, ToCliReport}; use crate::executable::BuildError as ExecutableBuildError; +use crate::executable::VariableDefinition; use crate::execution::{GraphQLError, Response}; use crate::schema::BuildError as SchemaBuildError; +use crate::schema::Implementers; use crate::SourceMap; use crate::{Node, NodeLocation}; use indexmap::IndexSet; +use std::collections::HashMap; use std::fmt; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; pub(crate) use crate::node::FileId; @@ -112,6 +116,70 @@ impl fmt::Display for Valid { } } +/// Shared context with things that may be used throughout executable validation. +#[derive(Debug)] +pub(crate) struct ExecutableValidationContext<'a> { + /// When None, rules that require a schema to validate are disabled. + schema: Option<&'a Schema>, + /// `schema.implementers_map()` is expensive to compute. This caches it for reuse. + implementers_map: OnceLock>, +} + +impl<'a> ExecutableValidationContext<'a> { + pub fn new(schema: Option<&'a Schema>) -> Self { + Self { + schema, + implementers_map: Default::default(), + } + } + + /// Returns the schema to validate against, if any. + pub fn schema(&self) -> Option<&'a Schema> { + self.schema + } + + /// Returns a cached reference to the implementers map. + pub fn implementers_map(&self) -> &HashMap { + self.implementers_map.get_or_init(|| { + self.schema + .map(|schema| schema.implementers_map()) + .unwrap_or_default() + }) + } + + /// Returns a context for operation validation. + pub fn operation_context<'o>( + &'o self, + variables: &'o [Node], + ) -> OperationValidationContext<'o> { + OperationValidationContext { + executable: self, + variables, + } + } +} + +/// Shared context when validating things inside an operation. +#[derive(Debug, Clone, Copy)] +pub(crate) struct OperationValidationContext<'a> { + /// Parent context. Using a reference so the `OnceLock` is shared between all operation + /// contexts. + executable: &'a ExecutableValidationContext<'a>, + /// The variables defined for this operation. + pub variables: &'a [Node], +} + +impl<'a> OperationValidationContext<'a> { + pub fn schema(&self) -> Option<&'a Schema> { + self.executable.schema + } + + /// Returns a cached reference to the implementers map. + pub fn implementers_map(&self) -> &HashMap { + self.executable.implementers_map() + } +} + /// A conversion failed with some errors, but also resulted in a partial document. /// /// The [`Debug`][fmt::Debug] trait is implemented by forwarding to [`Self::errors`] and diff --git a/crates/apollo-compiler/src/validation/operation.rs b/crates/apollo-compiler/src/validation/operation.rs index 484369441..f6e4b481c 100644 --- a/crates/apollo-compiler/src/validation/operation.rs +++ b/crates/apollo-compiler/src/validation/operation.rs @@ -1,42 +1,8 @@ -use std::collections::HashMap; -use std::sync::OnceLock; - -use crate::ast; -use crate::ast::Name; use crate::executable; -use crate::schema::Implementers; use crate::validation::DiagnosticList; +use crate::validation::ExecutableValidationContext; use crate::ExecutableDocument; use crate::Node; -use crate::Schema; - -#[derive(Debug)] -pub(crate) struct OperationValidationConfig<'a> { - /// When None, rules that require a schema to validate are disabled. - pub schema: Option<&'a Schema>, - /// The variables defined for this operation. - pub variables: &'a [Node], - implementers_map: OnceLock>, -} - -impl<'a> OperationValidationConfig<'a> { - pub fn new(schema: Option<&'a Schema>, variables: &'a [Node]) -> Self { - Self { - schema, - variables, - implementers_map: Default::default(), - } - } - - /// Returns a cached reference to the implementers map. - pub fn implementers_map(&self) -> &HashMap { - self.implementers_map.get_or_init(|| { - self.schema - .map(|schema| schema.implementers_map()) - .unwrap_or_default() - }) - } -} pub(crate) fn validate_subscription( document: &executable::ExecutableDocument, @@ -85,13 +51,11 @@ pub(crate) fn validate_subscription( pub(crate) fn validate_operation( diagnostics: &mut DiagnosticList, - schema: Option<&Schema>, document: &ExecutableDocument, operation: &executable::Operation, + context: &ExecutableValidationContext<'_>, ) { - let config = OperationValidationConfig::new(schema, &operation.variables); - - let against_type = if let Some(schema) = schema { + let against_type = if let Some(schema) = context.schema() { schema .root_operation(operation.operation_type) .map(|ty| (schema, ty)) @@ -101,14 +65,14 @@ pub(crate) fn validate_operation( super::directive::validate_directives( diagnostics, - schema, + context.schema(), operation.directives.iter(), operation.operation_type.into(), &operation.variables, ); super::variable::validate_variable_definitions( diagnostics, - config.schema, + context.schema(), &operation.variables, ); @@ -118,16 +82,16 @@ pub(crate) fn validate_operation( document, against_type, &operation.selection_set, - &config, + context.operation_context(&operation.variables), ); } pub(crate) fn validate_operation_definitions( diagnostics: &mut DiagnosticList, - schema: Option<&Schema>, document: &ExecutableDocument, + context: &ExecutableValidationContext<'_>, ) { for operation in document.all_operations() { - validate_operation(diagnostics, schema, document, operation); + validate_operation(diagnostics, document, operation, context); } } diff --git a/crates/apollo-compiler/src/validation/selection.rs b/crates/apollo-compiler/src/validation/selection.rs index 8741b02d5..ae34e5e0a 100644 --- a/crates/apollo-compiler/src/validation/selection.rs +++ b/crates/apollo-compiler/src/validation/selection.rs @@ -3,8 +3,8 @@ use crate::ast::NamedType; use crate::coordinate::TypeAttributeCoordinate; use crate::executable::BuildError; use crate::executable::SelectionSet; -use crate::validation::operation::OperationValidationConfig; use crate::validation::DiagnosticList; +use crate::validation::OperationValidationContext; use crate::ExecutableDocument; use crate::{ast, executable, schema, Node}; use apollo_parser::LimitTracker; @@ -545,7 +545,7 @@ pub(crate) fn validate_selection_set( document: &ExecutableDocument, against_type: Option<(&crate::Schema, &NamedType)>, selection_set: &SelectionSet, - context: &OperationValidationConfig<'_>, + context: OperationValidationContext<'_>, ) { for selection in &selection_set.selections { match selection { From 5b9875b5902eaeda436c6dfb70add0aa4025ed74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Wed, 5 Jun 2024 11:33:54 +0200 Subject: [PATCH 3/3] fix(compiler): add a benchmark for #862 --- crates/apollo-compiler/Cargo.toml | 5 ++ .../benches/fragments_validation.rs | 51 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 crates/apollo-compiler/benches/fragments_validation.rs diff --git a/crates/apollo-compiler/Cargo.toml b/crates/apollo-compiler/Cargo.toml index b2ca99a44..f476d00d2 100644 --- a/crates/apollo-compiler/Cargo.toml +++ b/crates/apollo-compiler/Cargo.toml @@ -53,6 +53,11 @@ name = "fields-validation" path = "benches/fields_validation.rs" harness = false +[[bench]] +name = "fragments-validation" +path = "benches/fragments_validation.rs" +harness = false + [[test]] name = "main" diff --git a/crates/apollo-compiler/benches/fragments_validation.rs b/crates/apollo-compiler/benches/fragments_validation.rs new file mode 100644 index 000000000..0e197a9ab --- /dev/null +++ b/crates/apollo-compiler/benches/fragments_validation.rs @@ -0,0 +1,51 @@ +use apollo_compiler::ExecutableDocument; +use apollo_compiler::Schema; +use criterion::*; +use std::fmt::Write; + +fn bench_big_schema_many_fragments(c: &mut Criterion) { + const NUM_INTERFACES: usize = 200; + const NUM_OBJECTS: usize = 10_000; + + let mut sdl = String::new(); + for i in 0..NUM_INTERFACES { + _ = writeln!(&mut sdl, r#"interface Intf{i} {{ field: Int! }}"#); + } + for o in 0..NUM_OBJECTS { + let i = o % NUM_INTERFACES; + _ = writeln!( + &mut sdl, + r#"type Ty{o} implements Intf{i} {{ field: Int! }}"# + ); + } + + _ = writeln!(&mut sdl, "type Query {{"); + for i in 0..NUM_INTERFACES { + _ = writeln!(&mut sdl, " intf{i}: Intf{i}"); + } + _ = writeln!(&mut sdl, "}}"); + + let schema = Schema::parse_and_validate(sdl, "schema.graphql").unwrap(); + let mut selection = String::new(); + let mut fragments = String::new(); + for i in 0..NUM_INTERFACES { + _ = writeln!(&mut selection, " intf{i} {{ ...frag{i} }}"); + _ = writeln!(&mut fragments, "fragment frag{i} on Ty{i} {{ field }}"); + } + let query = format!( + "query {{ + {selection}}} +{fragments}" + ); + + c.bench_function("big_schema_many_fragments", move |b| { + b.iter(|| { + let doc = + ExecutableDocument::parse_and_validate(&schema, &query, "query.graphql").unwrap(); + black_box(doc); + }); + }); +} + +criterion_group!(fragments, bench_big_schema_many_fragments,); +criterion_main!(fragments);