Skip to content

Commit

Permalink
chore: JIT executor (#2259)
Browse files Browse the repository at this point in the history
Co-authored-by: Sandipsinh Rathod <zotbysandip@gmail.com>
Co-authored-by: Sandipsinh Dilipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 26, 2024
1 parent c5a5094 commit a67feec
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 6 deletions.
6 changes: 6 additions & 0 deletions src/core/blueprint/dynamic_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,9 @@ impl TryFrom<&Value> for DynamicValue<ConstValue> {
}
}
}

impl<'a> From<&'a DynamicValue<serde_json::Value>> for serde_json_borrow::Value<'a> {
fn from(_value: &'a DynamicValue<serde_json::Value>) -> Self {
todo!()
}
}
2 changes: 1 addition & 1 deletion src/core/jit/common/json_placeholder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,6 @@ impl JsonPlaceholder {
store
});

Synth::new(plan.into_children(), store)
Synth::new(plan, store)
}
}
13 changes: 13 additions & 0 deletions src/core/jit/eval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use super::ExecutionPlan;
use crate::core::runtime::TargetRuntime;

struct Exec {
runtime: TargetRuntime,
plan: ExecutionPlan,
}

impl Exec {
pub fn new(runtime: TargetRuntime, plan: ExecutionPlan) -> Self {
Self { runtime, plan }
}
}
113 changes: 113 additions & 0 deletions src/core/jit/eval_ir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use serde_json_borrow::Value;

use crate::core::ir::Error;
use crate::core::jit::ir::IR;
use crate::core::runtime::TargetRuntime;

/// An async executor for the IR.
pub struct Eval {
#[allow(unused)]
runtime: TargetRuntime,
}

impl Eval {
pub fn new(runtime: TargetRuntime) -> Self {
Self { runtime }
}

#[async_recursion::async_recursion]
#[allow(clippy::only_used_in_recursion)]
pub async fn eval<'a>(
&'a self,
ir: &'a IR,
value: Option<Value<'a>>,
) -> Result<Value<'a>, Error> {
match ir {
IR::Path(path) => {
let value = value.unwrap_or(Value::Null);
let value = get_path(value, path).unwrap_or(Value::Null);
Ok(value)
}
IR::Dynamic(value) => Ok(Value::from(value)),
IR::IO(_) => todo!(),
IR::Cache(_) => todo!(),
IR::Protect => todo!(),
IR::Map(_) => todo!(),
IR::Pipe(first, second) => {
let first = self.eval(first, value).await?;
self.eval(second, Some(first)).await
}
}
}
}

fn get_path<'a, T: AsRef<str>>(value: Value<'a>, path: &'a [T]) -> Option<Value<'a>> {
let (head, tail) = path.split_first()?;
let value = match value {
Value::Object(map) => map
.into_vec()
.into_iter()
.find(|(k, _)| k == head.as_ref())
.map(|(_, v)| v),
Value::Array(arr) => {
let index = head.as_ref().parse::<usize>().ok()?;
arr.into_iter().nth(index)
}
_ => None,
};

if tail.is_empty() {
value
} else {
get_path(value?, tail)
}
}

#[cfg(test)]
mod tests {
use serde_json_borrow::ObjectAsVec;

use super::*;

#[test]
fn test_resolve_path_obj() {
let mut obj = ObjectAsVec::default();
obj.insert("a", Value::Str("b".into()));
let json = Value::Object(obj);

let path = vec!["a"];
let result = get_path(json, &path);
assert!(result.is_some());
assert_eq!(result.unwrap(), Value::Str("b".into()));
}

#[test]
fn test_resolve_path_arr() {
let arr = vec![
Value::Str("a".into()),
Value::Str("b".into()),
Value::Str("c".into()),
];

let json = Value::Array(arr);
let path = vec!["2"];
let result = get_path(json, &path);
assert!(result.is_some());
assert_eq!(result.unwrap(), Value::Str("c".into()));
}

#[test]
fn test_resolve_path_obj_and_arr() {
let mut obj = ObjectAsVec::default();
obj.insert("a", Value::Str("b".into()));
let json = Value::Object(obj);

let arr = vec![Value::Str("a".into()), json, Value::Str("c".into())];

let json = Value::Array(arr);
let path = vec!["1", "a"];
let result = get_path(json, &path);
assert!(result.is_some());
assert_eq!(result.unwrap(), Value::Str("b".into()));
}
}
62 changes: 62 additions & 0 deletions src/core/jit/ir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::collections::HashMap;
use std::num::NonZeroU64;

use crate::core::blueprint::DynamicValue;
use crate::core::config::group_by::GroupBy;
use crate::core::http::HttpFilter;
use crate::core::{graphql, grpc, http};

#[derive(Clone, Debug)]
pub enum IR {
Dynamic(DynamicValue<serde_json::Value>),
IO(IO),
Cache(Cache),
Path(Vec<String>),
Protect,
Map(Map),
Pipe(Box<IR>, Box<IR>),
}

#[derive(Clone, Debug)]
pub struct Map {
pub input: Box<IR>,
pub map: HashMap<String, String>,
}

#[derive(Clone, Debug)]
pub struct Cache {
pub max_age: NonZeroU64,
pub io: IO,
}

#[allow(unused)]
#[derive(Clone, Debug)]
pub struct IO {
pub group_by: Option<GroupBy>,
pub protocol: Protocol,
}

#[derive(Clone, Debug)]
pub enum Protocol {
Http {
template: http::RequestTemplate,
http_filter: Option<HttpFilter>,
},
GraphQL {
template: graphql::RequestTemplate,
field_name: String,
batch: bool,
},
Grpc {
req_template: grpc::RequestTemplate,
},
Script {
name: String,
},
}

impl Cache {
pub fn new(max_age: NonZeroU64, io: IO) -> Self {
Self { max_age, io }
}
}
3 changes: 3 additions & 0 deletions src/core/jit/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
mod builder;
mod eval_ir;
mod model;
mod store;
mod synth;
pub use builder::*;
pub use eval_ir::*;
pub use model::*;
pub use store::*;
pub use synth::*;

// NOTE: Only used in tests and benchmarks
pub mod common;
pub mod ir;
12 changes: 7 additions & 5 deletions src/core/jit/synth.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
use serde_json_borrow::{ObjectAsVec, Value};

use super::ExecutionPlan;
use crate::core::jit::model::{Children, Field};
use crate::core::jit::store::{Data, Store};

pub struct Synth<'a> {
operations: Vec<Field<Children>>,
selection: Vec<Field<Children>>,
store: Store<Value<'a>>,
}

impl<'a> Synth<'a> {
pub fn new(operations: Vec<Field<Children>>, store: Store<Value<'a>>) -> Self {
Self { operations, store }
pub fn new(plan: ExecutionPlan, store: Store<Value<'a>>) -> Self {
Self { selection: plan.into_children(), store }
}

pub fn synthesize(&self) -> Value {
let mut data = ObjectAsVec::default();

for child in self.operations.iter() {
for child in self.selection.iter() {
let val = self.iter(child, None, None);
data.insert(child.name.as_str(), val);
}
Expand Down Expand Up @@ -226,7 +228,7 @@ mod tests {
store
});

let synth = Synth::new(plan.into_children(), store);
let synth = Synth::new(plan, store);
let val = synth.synthesize();

serde_json::to_string_pretty(&val).unwrap()
Expand Down

1 comment on commit a67feec

@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 6.71ms 3.05ms 137.54ms 73.48%
Req/Sec 3.77k 194.04 4.50k 92.33%

450467 requests in 30.01s, 2.26GB read

Requests/sec: 15012.40

Transfer/sec: 77.05MB

Please sign in to comment.