Skip to content

Commit

Permalink
creating request in cache_key
Browse files Browse the repository at this point in the history
  • Loading branch information
shashitnak committed May 1, 2024
1 parent c7a25cb commit 6312159
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 46 deletions.
13 changes: 11 additions & 2 deletions benches/request_template_bench.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::collections::hash_map::DefaultHasher;

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use derive_setters::Setters;
Expand Down Expand Up @@ -49,13 +50,21 @@ fn benchmark_to_request(c: &mut Criterion) {

c.bench_function("with_mustache_literal", |b| {
b.iter(|| {
black_box(tmpl_literal.to_request(&ctx).unwrap());
black_box(
tmpl_literal
.to_request(&ctx, None::<DefaultHasher>)
.unwrap(),
);
})
});

c.bench_function("with_mustache_expressions", |b| {
b.iter(|| {
black_box(tmpl_mustache.to_request(&ctx).unwrap());
black_box(
tmpl_mustache
.to_request(&ctx, None::<DefaultHasher>)
.unwrap(),
);
})
});
}
Expand Down
97 changes: 54 additions & 43 deletions src/http/request_template.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::borrow::Cow;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex};

use derive_setters::Setters;
use hyper::HeaderMap;
Expand Down Expand Up @@ -28,6 +29,7 @@ pub struct RequestTemplate {
pub body_path: Option<Mustache>,
pub endpoint: Endpoint,
pub encoding: Encoding,
pub rendered_request: Arc<Mutex<Option<anyhow::Result<reqwest::Request>>>>,
}

impl RequestTemplate {
Expand Down Expand Up @@ -97,13 +99,19 @@ impl RequestTemplate {
pub fn to_request<C: PathString + HasHeaders>(
&self,
ctx: &C,
mut hasher: Option<impl Hasher>,
) -> anyhow::Result<reqwest::Request> {
// Create url
let url = self.create_url(ctx)?;
let method = self.method.clone();
if let Some(hasher) = hasher.as_mut() {
url.hash(hasher);
method.hash(hasher);
}
let mut req = reqwest::Request::new(method, url);
req = self.set_headers(req, ctx);
req = self.set_body(req, ctx)?;

req = self.set_headers(req, ctx, hasher.as_mut());
req = self.set_body(req, ctx, hasher.as_mut())?;

Ok(req)
}
Expand All @@ -113,11 +121,16 @@ impl RequestTemplate {
&self,
mut req: reqwest::Request,
ctx: &C,
mut hasher: Option<impl Hasher>,
) -> anyhow::Result<reqwest::Request> {
if let Some(body_path) = &self.body_path {
match &self.encoding {
Encoding::ApplicationJson => {
req.body_mut().replace(body_path.render(ctx).into());
let body = body_path.render(ctx);
if let Some(hasher) = hasher.as_mut() {
body.hash(hasher);
}
req.body_mut().replace(body.into());
}
Encoding::ApplicationXWwwFormUrlencoded => {
// TODO: this is a performance bottleneck
Expand All @@ -128,6 +141,9 @@ impl RequestTemplate {
Err(_) => body,
};

if let Some(hasher) = hasher.as_mut() {
form_data.hash(hasher);

Check warning on line 145 in src/http/request_template.rs

View check run for this annotation

Codecov / codecov/patch

src/http/request_template.rs#L145

Added line #L145 was not covered by tests
}
req.body_mut().replace(form_data.into());
}
}
Expand All @@ -140,6 +156,7 @@ impl RequestTemplate {
&self,
mut req: reqwest::Request,
ctx: &C,
mut hasher: Option<impl Hasher>,
) -> reqwest::Request {
let headers = self.create_headers(ctx);
if !headers.is_empty() {
Expand All @@ -163,6 +180,14 @@ impl RequestTemplate {
}

headers.extend(ctx.headers().to_owned());

if let Some(hasher) = hasher.as_mut() {
for (key, value) in headers.iter() {
key.hash(hasher);
value.hash(hasher);
}
}

req
}

Expand All @@ -175,6 +200,7 @@ impl RequestTemplate {
body_path: Default::default(),
endpoint: Endpoint::new(root_url.to_string()),
encoding: Default::default(),
rendered_request: Default::default(),
})
}

Expand Down Expand Up @@ -220,6 +246,7 @@ impl TryFrom<Endpoint> for RequestTemplate {
body_path: body,
endpoint,
encoding,
rendered_request: Default::default(),
})
}
}
Expand All @@ -229,27 +256,8 @@ impl<Ctx: PathString + HasHeaders> CacheKey<Ctx> for RequestTemplate {
let mut hasher = DefaultHasher::new();
let state = &mut hasher;

self.method.hash(state);

let mut headers = vec![];
for (name, mustache) in self.headers.iter() {
name.hash(state);
mustache.render(ctx).hash(state);
headers.push((name.to_string(), mustache.render(ctx)));
}

for (name, value) in ctx.headers().iter() {
name.hash(state);
value.hash(state);
headers.push((name.to_string(), value.to_str().unwrap().to_string()));
}

if let Some(body) = self.body_path.as_ref() {
body.render(ctx).hash(state)
}

let url = self.create_url(ctx).unwrap();
url.hash(state);
let request = self.to_request(ctx, Some(state));
self.rendered_request.lock().unwrap().replace(request);

hasher.finish()
}
Expand All @@ -258,6 +266,7 @@ impl<Ctx: PathString + HasHeaders> CacheKey<Ctx> for RequestTemplate {
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use std::collections::hash_map::DefaultHasher;

use derive_setters::Setters;
use hyper::header::HeaderName;
Expand Down Expand Up @@ -297,7 +306,7 @@ mod tests {
impl RequestTemplate {
fn to_body<C: PathString + HasHeaders>(&self, ctx: &C) -> anyhow::Result<String> {
let body = self
.to_request(ctx)?
.to_request(ctx, None::<DefaultHasher>)?
.body()
.and_then(|a| a.as_bytes())
.map(|a| a.to_vec())
Expand All @@ -311,15 +320,15 @@ mod tests {
fn test_url() {
let tmpl = RequestTemplate::new("http://localhost:3000/").unwrap();
let ctx = Context::default();
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(req.url().to_string(), "http://localhost:3000/");
}

#[test]
fn test_url_path() {
let tmpl = RequestTemplate::new("http://localhost:3000/foo/bar").unwrap();
let ctx = Context::default();
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(req.url().to_string(), "http://localhost:3000/foo/bar");
}

Expand All @@ -332,7 +341,7 @@ mod tests {
}
}));

let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(req.url().to_string(), "http://localhost:3000/foo/bar");
}

Expand All @@ -347,7 +356,7 @@ mod tests {
"booz": 1
}
}));
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(
req.url().to_string(),
"http://localhost:3000/foo/bar/boozes/1"
Expand All @@ -365,7 +374,7 @@ mod tests {
.unwrap()
.query(query);
let ctx = Context::default();
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(
req.url().to_string(),
"http://localhost:3000/?foo=0&bar=1&baz=2"
Expand All @@ -390,7 +399,7 @@ mod tests {
"id": 2
}
}));
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(
req.url().to_string(),
"http://localhost:3000/?foo=0&bar=1&baz=2"
Expand All @@ -417,7 +426,7 @@ mod tests {
.unwrap()
.headers(headers);
let ctx = Context::default();
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(req.headers().get("foo").unwrap(), "foo");
assert_eq!(req.headers().get("bar").unwrap(), "bar");
assert_eq!(req.headers().get("baz").unwrap(), "baz");
Expand Down Expand Up @@ -450,7 +459,7 @@ mod tests {
"id": 2
}
}));
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(req.headers().get("foo").unwrap(), "0");
assert_eq!(req.headers().get("bar").unwrap(), "1");
assert_eq!(req.headers().get("baz").unwrap(), "2");
Expand All @@ -463,7 +472,7 @@ mod tests {
.method(reqwest::Method::POST)
.encoding(crate::config::Encoding::ApplicationJson);
let ctx = Context::default();
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(
req.headers().get("Content-Type").unwrap(),
"application/json"
Expand All @@ -477,7 +486,7 @@ mod tests {
.method(reqwest::Method::POST)
.encoding(crate::config::Encoding::ApplicationXWwwFormUrlencoded);
let ctx = Context::default();
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(
req.headers().get("Content-Type").unwrap(),
"application/x-www-form-urlencoded"
Expand All @@ -490,7 +499,7 @@ mod tests {
.unwrap()
.method(reqwest::Method::POST);
let ctx = Context::default();
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(req.method(), reqwest::Method::POST);
}

Expand Down Expand Up @@ -534,6 +543,8 @@ mod tests {
}

mod endpoint {
use std::collections::hash_map::DefaultHasher;

use hyper::HeaderMap;
use serde_json::json;

Expand All @@ -550,7 +561,7 @@ mod tests {
.body(Some("foo".into()));
let tmpl = RequestTemplate::try_from(endpoint).unwrap();
let ctx = Context::default();
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(req.method(), reqwest::Method::POST);
assert_eq!(req.headers().get("foo").unwrap(), "bar");
let body = req.body().unwrap().as_bytes().unwrap().to_owned();
Expand All @@ -575,7 +586,7 @@ mod tests {
"header": "abc"
}
}));
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(req.method(), reqwest::Method::POST);
assert_eq!(req.headers().get("foo").unwrap(), "abc");
let body = req.body().unwrap().as_bytes().unwrap().to_owned();
Expand All @@ -589,7 +600,7 @@ mod tests {
crate::endpoint::Endpoint::new("http://localhost:3000/?a={{args.a}}".to_string());
let tmpl = RequestTemplate::try_from(endpoint).unwrap();
let ctx = Context::default();
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(req.url().to_string(), "http://localhost:3000/");
}

Expand All @@ -604,7 +615,7 @@ mod tests {
]);
let tmpl = RequestTemplate::try_from(endpoint).unwrap();
let ctx = Context::default();
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(req.url().to_string(), "http://localhost:3000/?q=1&b=1");
}

Expand All @@ -620,7 +631,7 @@ mod tests {
"d": "bar"
}
}));
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(
req.url().to_string(),
"http://localhost:3000/foo?b=foo&d=bar"
Expand All @@ -644,7 +655,7 @@ mod tests {
"f": "baz"
}
}));
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(
req.url().to_string(),
"http://localhost:3000/foo?b=foo&d=bar&f=baz"
Expand All @@ -658,7 +669,7 @@ mod tests {
let mut headers = HeaderMap::new();
headers.insert("baz", "qux".parse().unwrap());
let ctx = Context::default().headers(headers);
let req = tmpl.to_request(&ctx).unwrap();
let req = tmpl.to_request(&ctx, None::<DefaultHasher>).unwrap();
assert_eq!(req.headers().get("baz").unwrap(), "qux");
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/lambda/io.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::future::Future;
use std::collections::hash_map::DefaultHasher;
use std::pin::Pin;
use std::sync::Arc;

Expand Down Expand Up @@ -76,7 +77,12 @@ impl IO {
Box::pin(async move {
match self {
IO::Http { req_template, dl_id, .. } => {
let req = req_template.to_request(&ctx)?;
let req = req_template
.rendered_request
.lock()
.unwrap()
.take()
.unwrap_or(req_template.to_request(&ctx, None::<DefaultHasher>))?;
let is_get = req.method() == reqwest::Method::GET;

let res = if is_get && ctx.request_ctx.is_batching_enabled() {
Expand Down

1 comment on commit 6312159

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running 30s test @ http://localhost:8000/graphql

4 threads and 100 connections

Thread Stats Avg Stdev Max +/- Stdev
Latency 7.72ms 4.04ms 133.19ms 78.50%
Req/Sec 3.30k 262.71 4.55k 89.58%

394394 requests in 30.02s, 1.98GB read

Requests/sec: 13136.91

Transfer/sec: 67.43MB

Please sign in to comment.