graphql-engine/v3/crates/engine/benches/execute.rs
Daniel Harvey 9d25ee2075 Add time limits on generate_ir benchmarks too (#508)
<!-- Thank you for submitting this PR! :) -->

## Description

I wondered why some benchmarks were very quick seconds and others very
slow (hours). Turns out I only told half of them that I would prefer
them to be fast. Rectified.

Functional no-op.

V3_GIT_ORIGIN_REV_ID: 40bbcc6829e9a4ea85058f2983136024a9971e3b
2024-04-25 15:43:52 +00:00

233 lines
7.5 KiB
Rust

use core::time::Duration;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, SamplingMode};
use engine::execute::plan::{execute_mutation_plan, execute_query_plan, generate_request_plan};
use engine::execute::{execute_query_internal, generate_ir, HttpContext};
use engine::schema::GDS;
use hasura_authn_core::Identity;
use lang_graphql::http::RawRequest;
use open_dds::permissions::Role;
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use tokio::runtime::Runtime;
extern crate json_value_merge;
use json_value_merge::Merge;
use serde_json::Value;
use std::path::Path;
use lang_graphql as gql;
pub fn merge_with_common_metadata(
common_metadata_path: &Path,
metadata_path_string: &Path,
) -> Value {
let common_metadata = fs::read_to_string(common_metadata_path).unwrap();
let test_metadata = fs::read_to_string(metadata_path_string).unwrap();
let mut first_json_value: Value = serde_json::from_str(&common_metadata.to_string()).unwrap();
let second_json_value: Value = serde_json::from_str(&test_metadata.to_string()).unwrap();
first_json_value.merge(&second_json_value);
first_json_value
}
pub fn bench_execute(
c: &mut Criterion,
test_path_string: &str,
common_metadata_path_string: &str,
benchmark_group: &str,
) {
let root_test_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests");
let test_path = root_test_dir.join(test_path_string);
let request_path = test_path.join("request.gql");
let common_metadata_path = root_test_dir.join(common_metadata_path_string);
let metadata_path = test_path.join("metadata.json");
let metadata = merge_with_common_metadata(&metadata_path, &common_metadata_path);
let gds = GDS::new(open_dds::traits::OpenDd::deserialize(metadata).unwrap()).unwrap();
let schema = GDS::build_schema(&gds).unwrap();
let http_context = HttpContext {
client: reqwest::Client::new(),
ndc_response_size_limit: None,
};
let runtime = Runtime::new().unwrap();
let query = fs::read_to_string(request_path).unwrap();
let raw_request = RawRequest {
operation_name: None,
query,
variables: None,
};
let session = Identity::admin(Role::new("admin"))
.get_role_authorization(None)
.unwrap()
.build_session(HashMap::new());
let mut group = c.benchmark_group(benchmark_group);
// these numbers are fairly low, optimising for runtime of benchmark suite
group.warm_up_time(Duration::from_millis(500));
group.sample_size(20);
group.sampling_mode(SamplingMode::Flat);
// Parse request
group.bench_with_input(
BenchmarkId::new("bench", "Resolution of raw request"),
&(&runtime, &raw_request),
|b, (runtime, request)| {
b.to_async(*runtime).iter(|| async {
gql::parser::Parser::new(&request.query)
.parse_executable_document()
.unwrap()
})
},
);
let query = gql::parser::Parser::new(&raw_request.query)
.parse_executable_document()
.unwrap();
// Normalize request
let request = gql::http::Request {
operation_name: None,
query,
variables: HashMap::default(),
};
group.bench_with_input(
BenchmarkId::new("bench_execute", "Normalize request"),
&(&runtime, &schema, &request),
|b, (runtime, schema, request)| {
b.to_async(*runtime).iter(|| async {
gql::validation::normalize_request(&session.role, schema, request).unwrap();
})
},
);
let normalized_request =
gql::validation::normalize_request(&session.role, &schema, &request).unwrap();
// Generate IR
group.bench_with_input(
BenchmarkId::new("bench_execute", "Generate IR"),
&(&runtime, &schema),
|b, (runtime, schema)| {
b.to_async(*runtime)
.iter(|| async { generate_ir(schema, &session, &normalized_request).unwrap() })
},
);
let ir = generate_ir(&schema, &session, &normalized_request).unwrap();
// Generate Query Plan
group.bench_with_input(
BenchmarkId::new("bench_execute", "Generate Query Plan"),
&(&runtime),
|b, runtime| {
b.to_async(*runtime)
.iter(|| async { generate_request_plan(&ir).unwrap() })
},
);
// Execute Query plan
group.bench_with_input(
BenchmarkId::new("bench_execute", "Execute Query Plan"),
&(&runtime),
|b, runtime| {
b.to_async(*runtime).iter(|| async {
match generate_request_plan(&ir).unwrap() {
engine::execute::plan::RequestPlan::QueryPlan(query_plan) => {
execute_query_plan(&http_context, query_plan, None).await
}
engine::execute::plan::RequestPlan::MutationPlan(mutation_plan) => {
execute_mutation_plan(&http_context, mutation_plan, None).await
}
}
})
},
);
// Total execution time from start to finish
group.bench_with_input(
BenchmarkId::new("bench_execute", "Total Execution time"),
&(&runtime, &schema, raw_request),
|b, (runtime, schema, request)| {
b.to_async(*runtime).iter(|| async {
execute_query_internal(&http_context, schema, &session, request.clone(), None)
.await
.unwrap()
})
},
);
group.finish();
}
fn bench_execute_all(c: &mut Criterion) {
// Simple select
let test_path_string = "execute/models/select_one/simple_select";
let common_metadata_path_string = "execute/common_metadata/postgres_connector_schema.json";
bench_execute(
c,
test_path_string,
common_metadata_path_string,
"simple_select",
);
// Select Many
let test_path_string = "execute/models/select_many/simple_select";
let common_metadata_path_string = "execute/common_metadata/postgres_connector_schema.json";
bench_execute(
c,
test_path_string,
common_metadata_path_string,
"select_many",
);
// Select Many with where clause
let test_path_string = "execute/models/select_many/where/simple";
let common_metadata_path_string = "execute/common_metadata/postgres_connector_schema.json";
bench_execute(
c,
test_path_string,
common_metadata_path_string,
"select_many_where",
);
// Object Relationships
let test_path_string = "execute/relationships/object";
let common_metadata_path_string = "execute/common_metadata/postgres_connector_schema.json";
bench_execute(
c,
test_path_string,
common_metadata_path_string,
"object_relationship",
);
// Array Relationships
let test_path_string = "execute/relationships/array";
let common_metadata_path_string = "execute/common_metadata/postgres_connector_schema.json";
bench_execute(
c,
test_path_string,
common_metadata_path_string,
"array_relationship",
);
// Relay node field
let test_path_string = "execute/relay/relay";
let common_metadata_path_string = "execute/common_metadata/postgres_connector_schema.json";
bench_execute(
c,
test_path_string,
common_metadata_path_string,
"relay_node_field",
);
}
criterion_group!(benches, bench_execute_all);
criterion_main!(benches);