Skip to content

Commit

Permalink
bench partial
Browse files Browse the repository at this point in the history
  • Loading branch information
ssddOnTop committed May 1, 2024
1 parent df061bb commit b2346b3
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 10 deletions.
214 changes: 214 additions & 0 deletions benches/jq.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::time::Duration;

use async_graphql::context::SelectionField;
use async_graphql::{Name, Value};
use async_trait::async_trait;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use http_cache_reqwest::{Cache, CacheMode, HttpCache, HttpCacheOptions, MokaManager};
use hyper::body::Bytes;
use hyper::header::HeaderValue;
use hyper::HeaderMap;
use indexmap::IndexMap;
use once_cell::sync::Lazy;
use reqwest::{Client, Request};
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use tailcall::blueprint::{Server, Upstream};
use tailcall::cache::InMemoryCache;
use tailcall::http::{RequestContext, Response};
use tailcall::lambda::{EvaluationContext, ResolverContextLike};
use tailcall::path::PathString;
use tailcall::runtime::TargetRuntime;
use tailcall::{EnvIO, FileIO, HttpIO};
use tailcall::mustache::Mustache;

struct Http {
client: ClientWithMiddleware,
http2_only: bool,
}

impl Http {
fn init(upstream: &Upstream) -> Self {
let mut builder = Client::builder()
.tcp_keepalive(Some(Duration::from_secs(upstream.tcp_keep_alive)))
.timeout(Duration::from_secs(upstream.timeout))
.connect_timeout(Duration::from_secs(upstream.connect_timeout))
.http2_keep_alive_interval(Some(Duration::from_secs(upstream.keep_alive_interval)))
.http2_keep_alive_timeout(Duration::from_secs(upstream.keep_alive_timeout))
.http2_keep_alive_while_idle(upstream.keep_alive_while_idle)
.pool_idle_timeout(Some(Duration::from_secs(upstream.pool_idle_timeout)))
.pool_max_idle_per_host(upstream.pool_max_idle_per_host)
.user_agent(upstream.user_agent.clone());

// Add Http2 Prior Knowledge
if upstream.http2_only {
builder = builder.http2_prior_knowledge();
}

// Add Http Proxy
if let Some(ref proxy) = upstream.proxy {
builder = builder.proxy(
reqwest::Proxy::http(proxy.url.clone())
.expect("Failed to set proxy in http client"),
);
}

let mut client = ClientBuilder::new(builder.build().expect("Failed to build client"));

if upstream.http_cache {
client = client.with(Cache(HttpCache {
mode: CacheMode::Default,
manager: MokaManager::default(),
options: HttpCacheOptions::default(),
}))
}
Self { client: client.build(), http2_only: upstream.http2_only }
}
}

#[async_trait]
impl HttpIO for Http {
async fn execute(&self, mut request: Request) -> anyhow::Result<Response<Bytes>> {
if self.http2_only {
*request.version_mut() = reqwest::Version::HTTP_2;
}
let resp = self.client.execute(request).await?;
Response::from_reqwest(resp).await
}
}

struct Env {}

impl EnvIO for Env {
fn get(&self, _: &str) -> Option<Cow<'_, str>> {
unimplemented!("Not needed for this bench")
}
}

struct File;
#[async_trait]
impl FileIO for File {
async fn write<'a>(&'a self, _: &'a str, _: &'a [u8]) -> anyhow::Result<()> {
unimplemented!("Not needed for this bench")
}

async fn read<'a>(&'a self, _: &'a str) -> anyhow::Result<String> {
unimplemented!("Not needed for this bench")
}
}

const INPUT_VALUE: &[&[&str]] = &[
// existing values
&["{{jq:.value}}"],
&["value", "nested", "existing"],
// missing values
&["value", "missing"],
&["value", "nested", "missing"],
];

const ARGS_VALUE: &[&[&str]] = &[
// existing values
&["args", "root"],
&["args", "nested", "existing"],
// missing values
&["args", "missing"],
&["args", "nested", "missing"],
];

const HEADERS_VALUE: &[&[&str]] = &[&["headers", "existing"], &["headers", "missing"]];

const VARS_VALUE: &[&[&str]] = &[&["vars", "existing"], &["vars", "missing"]];

static TEST_VALUES: Lazy<Value> = Lazy::new(|| {
let mut root = IndexMap::new();
let mut nested = IndexMap::new();

nested.insert(
Name::new("existing"),
Value::String("nested-test".to_owned()),
);

root.insert(Name::new("root"), Value::String("root-test".to_owned()));
root.insert(Name::new("nested"), Value::Object(nested));

Value::Object(root)
});

static TEST_ARGS: Lazy<IndexMap<Name, Value>> = Lazy::new(|| {
let mut root = IndexMap::new();
let mut nested = IndexMap::new();

nested.insert(
Name::new("existing"),
Value::String("nested-test".to_owned()),
);

root.insert(Name::new("root"), Value::String("root-test".to_owned()));
root.insert(Name::new("nested"), Value::Object(nested));

root
});

static TEST_HEADERS: Lazy<HeaderMap> = Lazy::new(|| {
let mut map = HeaderMap::new();

map.insert("x-existing", HeaderValue::from_static("header"));

map
});

static TEST_VARS: Lazy<BTreeMap<String, String>> = Lazy::new(|| {
let mut map = BTreeMap::new();

map.insert("existing".to_owned(), "var".to_owned());

map
});

fn to_bench_id(input: &[&str]) -> BenchmarkId {
BenchmarkId::new("input", input.join("."))
}

#[derive(Clone)]
struct MockGraphqlContext;

impl<'a> ResolverContextLike<'a> for MockGraphqlContext {
fn value(&'a self) -> Option<&'a Value> {
Some(&TEST_VALUES)
}

fn args(&'a self) -> Option<&'a IndexMap<Name, Value>> {
Some(&TEST_ARGS)
}

fn field(&'a self) -> Option<SelectionField> {
None
}

fn add_error(&'a self, _: async_graphql::ServerError) {}
}


fn mustache(query: &str) -> Mustache {
Mustache::parse(query).unwrap()
}

fn bench_main(c: &mut Criterion) {

let all_inputs = INPUT_VALUE
.iter()
.chain(ARGS_VALUE)
.chain(HEADERS_VALUE)
.chain(VARS_VALUE);

for input in all_inputs {
c.bench_with_input(to_bench_id(input), input, |b, input| {
b.iter(|| eval_ctx.path_string(input));
});
}
}

criterion_group!(benches, bench_main);
criterion_main!(benches);
13 changes: 3 additions & 10 deletions src/serde_value_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,23 +212,16 @@ mod tests {

#[test]
fn test_render_jq_sort() {
let value = json!({"a": "{{jq: sort_by(.age) }}"});
let value = json!({"a": "{{jq: sort_by(.age) | .[0].name }}"});
let value = DynamicValue::try_from(&value).unwrap();
let ctx = json!([
{"name": "Alice", "age": 25, "city": "New York"},
{"name": "Bob", "age": 30, "city": "Chicago"},
{"name": "Sandip", "age": 16, "city": "GoodQuestion"},
{"name": "Charlie", "age": 22, "city": "San Francisco"}
]);
let result = value.render_value(&ctx);

let expected = json!({
"a": [
{"name": "Charlie", "age": 22, "city": "San Francisco"},
{"name": "Alice", "age": 25, "city": "New York"},
{"name": "Bob", "age": 30, "city": "Chicago"},
]
});
let expected = async_graphql::Value::from_json(expected).unwrap();
let expected = async_graphql::Value::from_json(json!({"a": "Sandip"})).unwrap();
assert_eq!(result.unwrap(), expected);
}

Expand Down

0 comments on commit b2346b3

Please sign in to comment.