Skip to content

Commit

Permalink
graphql: cache GraphQL validation (#3759)
Browse files Browse the repository at this point in the history
* graphql: cache graphql validation

* graphql: unlock GRAPHQL_VALIDATION_CACHE quickly

* graphql: minimize lock holding in validate_query

Co-authored-by: Leonardo Yvens <leoyvens@gmail.com>
  • Loading branch information
kamilkisiela and leoyvens authored Jul 26, 2022
1 parent 42a4788 commit b77bf9d
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 27 deletions.
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion graphql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2021"
crossbeam = "0.8"
graph = { path = "../graph" }
graphql-parser = "0.4.0"
graphql-tools = "0.0.19"
graphql-tools = { git = "https://github.com/dotansimha/graphql-tools-rs", branch = "clone-validation-error" }
indexmap = "1.9"
Inflector = "0.11.3"
lazy_static = "1.2.0"
Expand Down
80 changes: 56 additions & 24 deletions graphql/src/execution/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ use graph::data::graphql::DocumentExt as _;
use graph::data::value::Object;
use graphql_parser::Pos;
use graphql_tools::validation::rules::*;
use graphql_tools::validation::utils::ValidationError;
use graphql_tools::validation::validate::{validate, ValidationPlan};
use lazy_static::lazy_static;
use parking_lot::Mutex;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::hash::{Hash, Hasher};
use std::iter::FromIterator;
Expand All @@ -25,6 +27,11 @@ use crate::schema::ast::{self as sast};
use crate::values::coercion;
use crate::{execution::get_field, schema::api::ErrorPolicy};

lazy_static! {
static ref GRAPHQL_VALIDATION_CACHE: Mutex<HashMap<u64, Vec<ValidationError>>> =
Mutex::new(HashMap::<u64, Vec<ValidationError>>::new());
}

lazy_static! {
static ref GRAPHQL_VALIDATION_PLAN: ValidationPlan =
ValidationPlan::from(if !ENV_VARS.graphql.enable_validations {
Expand Down Expand Up @@ -137,6 +144,54 @@ pub struct Query {
pub query_id: String,
}

fn validate_query(
logger: &Logger,
query: &GraphDataQuery,
document: &s::Document,
) -> Result<(), Vec<QueryExecutionError>> {
let errors = {
let cached = GRAPHQL_VALIDATION_CACHE
.lock()
.get(&query.shape_hash)
.cloned();
match cached {
Some(cached) => cached,
None => {
let validation_errors =
validate(&document, &query.document, &GRAPHQL_VALIDATION_PLAN);
GRAPHQL_VALIDATION_CACHE
.lock()
.insert(query.shape_hash, validation_errors.clone());
validation_errors
}
}
};

if !errors.is_empty() {
if !ENV_VARS.graphql.silent_graphql_validations {
return Err(errors
.into_iter()
.map(|e| {
QueryExecutionError::ValidationError(
e.locations.first().cloned(),
e.message.clone(),
)
})
.collect());
} else {
warn!(
&logger,
"GraphQL Validation failure";
"query" => &query.query_text,
"variables" => &query.variables_text,
"errors" => format!("[{:?}]", errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join(", "))
);
}
}

Ok(())
}

impl Query {
/// Process the raw GraphQL query `query` and prepare for executing it.
/// The returned `Query` has already been validated and, if `max_complexity`
Expand All @@ -150,30 +205,7 @@ impl Query {
max_complexity: Option<u64>,
max_depth: u8,
) -> Result<Arc<Self>, Vec<QueryExecutionError>> {
let validation_errors =
validate(schema.document(), &query.document, &GRAPHQL_VALIDATION_PLAN);

if !validation_errors.is_empty() {
if !ENV_VARS.graphql.silent_graphql_validations {
return Err(validation_errors
.into_iter()
.map(|e| {
QueryExecutionError::ValidationError(
e.locations.first().cloned(),
e.message,
)
})
.collect());
} else {
warn!(
&logger,
"GraphQL Validation failure";
"query" => &query.query_text,
"variables" => &query.variables_text,
"errors" => format!("[{:?}]", validation_errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join(", "))
);
}
}
validate_query(logger, &query, &schema.document())?;

let mut operation = None;
let mut fragments = HashMap::new();
Expand Down

0 comments on commit b77bf9d

Please sign in to comment.