Skip to content

Commit b77bf9d

Browse files
graphql: cache GraphQL validation (#3759)
* graphql: cache graphql validation * graphql: unlock GRAPHQL_VALIDATION_CACHE quickly * graphql: minimize lock holding in validate_query Co-authored-by: Leonardo Yvens <[email protected]>
1 parent 42a4788 commit b77bf9d

File tree

3 files changed

+58
-27
lines changed

3 files changed

+58
-27
lines changed

Cargo.lock

+1-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

graphql/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ edition = "2021"
77
crossbeam = "0.8"
88
graph = { path = "../graph" }
99
graphql-parser = "0.4.0"
10-
graphql-tools = "0.0.19"
10+
graphql-tools = { git = "https://github.com/dotansimha/graphql-tools-rs", branch = "clone-validation-error" }
1111
indexmap = "1.9"
1212
Inflector = "0.11.3"
1313
lazy_static = "1.2.0"

graphql/src/execution/query.rs

+56-24
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ use graph::data::graphql::DocumentExt as _;
22
use graph::data::value::Object;
33
use graphql_parser::Pos;
44
use graphql_tools::validation::rules::*;
5+
use graphql_tools::validation::utils::ValidationError;
56
use graphql_tools::validation::validate::{validate, ValidationPlan};
67
use lazy_static::lazy_static;
8+
use parking_lot::Mutex;
79
use std::collections::{BTreeMap, HashMap, HashSet};
810
use std::hash::{Hash, Hasher};
911
use std::iter::FromIterator;
@@ -25,6 +27,11 @@ use crate::schema::ast::{self as sast};
2527
use crate::values::coercion;
2628
use crate::{execution::get_field, schema::api::ErrorPolicy};
2729

30+
lazy_static! {
31+
static ref GRAPHQL_VALIDATION_CACHE: Mutex<HashMap<u64, Vec<ValidationError>>> =
32+
Mutex::new(HashMap::<u64, Vec<ValidationError>>::new());
33+
}
34+
2835
lazy_static! {
2936
static ref GRAPHQL_VALIDATION_PLAN: ValidationPlan =
3037
ValidationPlan::from(if !ENV_VARS.graphql.enable_validations {
@@ -137,6 +144,54 @@ pub struct Query {
137144
pub query_id: String,
138145
}
139146

147+
fn validate_query(
148+
logger: &Logger,
149+
query: &GraphDataQuery,
150+
document: &s::Document,
151+
) -> Result<(), Vec<QueryExecutionError>> {
152+
let errors = {
153+
let cached = GRAPHQL_VALIDATION_CACHE
154+
.lock()
155+
.get(&query.shape_hash)
156+
.cloned();
157+
match cached {
158+
Some(cached) => cached,
159+
None => {
160+
let validation_errors =
161+
validate(&document, &query.document, &GRAPHQL_VALIDATION_PLAN);
162+
GRAPHQL_VALIDATION_CACHE
163+
.lock()
164+
.insert(query.shape_hash, validation_errors.clone());
165+
validation_errors
166+
}
167+
}
168+
};
169+
170+
if !errors.is_empty() {
171+
if !ENV_VARS.graphql.silent_graphql_validations {
172+
return Err(errors
173+
.into_iter()
174+
.map(|e| {
175+
QueryExecutionError::ValidationError(
176+
e.locations.first().cloned(),
177+
e.message.clone(),
178+
)
179+
})
180+
.collect());
181+
} else {
182+
warn!(
183+
&logger,
184+
"GraphQL Validation failure";
185+
"query" => &query.query_text,
186+
"variables" => &query.variables_text,
187+
"errors" => format!("[{:?}]", errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join(", "))
188+
);
189+
}
190+
}
191+
192+
Ok(())
193+
}
194+
140195
impl Query {
141196
/// Process the raw GraphQL query `query` and prepare for executing it.
142197
/// The returned `Query` has already been validated and, if `max_complexity`
@@ -150,30 +205,7 @@ impl Query {
150205
max_complexity: Option<u64>,
151206
max_depth: u8,
152207
) -> Result<Arc<Self>, Vec<QueryExecutionError>> {
153-
let validation_errors =
154-
validate(schema.document(), &query.document, &GRAPHQL_VALIDATION_PLAN);
155-
156-
if !validation_errors.is_empty() {
157-
if !ENV_VARS.graphql.silent_graphql_validations {
158-
return Err(validation_errors
159-
.into_iter()
160-
.map(|e| {
161-
QueryExecutionError::ValidationError(
162-
e.locations.first().cloned(),
163-
e.message,
164-
)
165-
})
166-
.collect());
167-
} else {
168-
warn!(
169-
&logger,
170-
"GraphQL Validation failure";
171-
"query" => &query.query_text,
172-
"variables" => &query.variables_text,
173-
"errors" => format!("[{:?}]", validation_errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join(", "))
174-
);
175-
}
176-
}
208+
validate_query(logger, &query, &schema.document())?;
177209

178210
let mut operation = None;
179211
let mut fragments = HashMap::new();

0 commit comments

Comments
 (0)