Skip to content

Commit

Permalink
fix building simple executor plan
Browse files Browse the repository at this point in the history
  • Loading branch information
meskill committed Apr 10, 2024
1 parent 282e58a commit 3c7f1ec
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 42 deletions.
180 changes: 168 additions & 12 deletions src/query_plan/execution/execution.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use std::fmt::{Display, Write};
use std::{

Check warning on line 1 in src/query_plan/execution/execution.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/query_plan/execution/execution.rs
fmt::{Display, Write},
mem::{discriminant, Discriminant},
};

use indenter::indented;

use super::super::resolver::Id;

#[derive(Debug, PartialEq, Eq)]
pub enum ExecutionStep {
Resolve(Id),
ForEach(Id),
Sequential(Vec<ExecutionStep>),
Parallel(Vec<ExecutionStep>),
}
Expand All @@ -14,11 +19,12 @@ impl Display for ExecutionStep {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ExecutionStep::Resolve(id) => writeln!(f, "Resolve({id})"),
ExecutionStep::ForEach(id) => writeln!(f, "ForEach({id})"),
ExecutionStep::Sequential(steps) | ExecutionStep::Parallel(steps) => {
match &self {
ExecutionStep::Sequential(_) => writeln!(f, "Sequential:"),
ExecutionStep::Parallel(_) => writeln!(f, "Parallel:"),
ExecutionStep::Resolve(_) => unreachable!(),
_ => unreachable!(),
}?;
let f = &mut indented(f);

Expand All @@ -33,19 +39,169 @@ impl Display for ExecutionStep {
}

impl ExecutionStep {
pub fn parallel(mut steps: Vec<ExecutionStep>) -> Self {
if steps.len() == 1 {
steps.pop().unwrap()
} else {
ExecutionStep::Parallel(steps)
fn inner_flatten(dscr: Discriminant<Self>, steps: Vec<Self>) -> Vec<Self> {
let mut result = Vec::with_capacity(steps.len());

for step in steps {
let step = step.flatten();
if dscr == discriminant(&step) {
match step {
ExecutionStep::Sequential(sub_steps) | ExecutionStep::Parallel(sub_steps) => {
for sub_step in sub_steps {
result.push(sub_step);
}
}
_ => unreachable!(),
}
} else {
if !step.is_empty() {
result.push(step);
}
}
}

result
}

pub fn sequential(mut steps: Vec<ExecutionStep>) -> Self {
if steps.len() == 1 {
steps.pop().unwrap()
} else {
ExecutionStep::Sequential(steps)
pub fn is_empty(&self) -> bool {
match self {
ExecutionStep::Sequential(steps) | ExecutionStep::Parallel(steps) => steps.is_empty(),
_ => false,
}
}

pub fn flatten(self) -> Self {
let dscr = discriminant(&self);
match self {
ExecutionStep::Resolve(_) | ExecutionStep::ForEach(_) => self,
ExecutionStep::Sequential(steps) => {
let mut steps = Self::inner_flatten(dscr, steps);

if steps.len() == 1 {
steps.pop().unwrap()
} else {
ExecutionStep::Sequential(steps)
}
}
ExecutionStep::Parallel(steps) => {
let mut steps = Self::inner_flatten(dscr, steps);

if steps.len() == 1 {
steps.pop().unwrap()
} else {
ExecutionStep::Parallel(steps)
}
}
}
}
}

#[cfg(test)]

Check warning on line 99 in src/query_plan/execution/execution.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/query_plan/execution/execution.rs
mod tests {
mod flatten {
use crate::query_plan::{execution::execution::ExecutionStep, resolver::Id};

#[test]
fn empty() {
assert_eq!(
ExecutionStep::Sequential(vec![]).flatten(),
ExecutionStep::Sequential(vec![])
);
assert_eq!(
ExecutionStep::Parallel(vec![]).flatten(),
ExecutionStep::Parallel(vec![])
);
}

#[test]
fn single() {
assert_eq!(
ExecutionStep::Resolve(Id(0)).flatten(),
ExecutionStep::Resolve(Id(0))
);

assert_eq!(
ExecutionStep::Sequential(vec![ExecutionStep::Resolve(Id(0))]).flatten(),
ExecutionStep::Resolve(Id(0))
);

assert_eq!(
ExecutionStep::Parallel(vec![ExecutionStep::Resolve(Id(0))]).flatten(),
ExecutionStep::Resolve(Id(0))
);
}

#[test]
fn sequential() {
assert_eq!(
ExecutionStep::Sequential(vec![
ExecutionStep::Resolve(Id(0)),
ExecutionStep::Sequential(vec![
ExecutionStep::Resolve(Id(1)),
ExecutionStep::Parallel(vec![]),
ExecutionStep::Resolve(Id(2)),
]),
ExecutionStep::Resolve(Id(3)),
ExecutionStep::Parallel(vec![
ExecutionStep::Resolve(Id(4)),
ExecutionStep::Resolve(Id(5)),
]),
ExecutionStep::Sequential(vec![ExecutionStep::Sequential(vec![
ExecutionStep::Resolve(Id(6)),
ExecutionStep::Resolve(Id(7))
])])
])
.flatten(),
ExecutionStep::Sequential(vec![
ExecutionStep::Resolve(Id(0)),
ExecutionStep::Resolve(Id(1)),
ExecutionStep::Resolve(Id(2)),
ExecutionStep::Resolve(Id(3)),
ExecutionStep::Parallel(vec![
ExecutionStep::Resolve(Id(4)),
ExecutionStep::Resolve(Id(5)),
]),
ExecutionStep::Resolve(Id(6)),
ExecutionStep::Resolve(Id(7))
])
);
}

#[test]
fn parallel() {
assert_eq!(
ExecutionStep::Parallel(vec![
ExecutionStep::Parallel(vec![
ExecutionStep::Resolve(Id(0)),
ExecutionStep::Resolve(Id(1)),
ExecutionStep::Resolve(Id(2)),
]),
ExecutionStep::Resolve(Id(3)),
ExecutionStep::Sequential(vec![
ExecutionStep::Resolve(Id(4)),
ExecutionStep::Parallel(vec![
ExecutionStep::Resolve(Id(5)),
ExecutionStep::Resolve(Id(6)),
]),
ExecutionStep::Resolve(Id(7))
])
])
.flatten(),
ExecutionStep::Parallel(vec![
ExecutionStep::Resolve(Id(0)),
ExecutionStep::Resolve(Id(1)),
ExecutionStep::Resolve(Id(2)),
ExecutionStep::Resolve(Id(3)),
ExecutionStep::Sequential(vec![
ExecutionStep::Resolve(Id(4)),
ExecutionStep::Parallel(vec![
ExecutionStep::Resolve(Id(5)),
ExecutionStep::Resolve(Id(6)),
]),
ExecutionStep::Resolve(Id(7))
])
])
)
}
}
}
3 changes: 3 additions & 0 deletions src/query_plan/execution/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ impl<'a> ExecutorContext<'a> {

self.resolved.insert(*id, result);
}
ExecutionStep::ForEach(id) => {

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Check Examples

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Tests (WASM)

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Tests on linux-arm64-gnu

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Tests on linux-x64-musl

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Tests on linux-arm64-musl

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Tests on linux-x64-gnu

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Tests on win32-ia32-gnu

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Tests on linux-ia32-gnu

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Tests on darwin-arm64

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Test AWS Lambda Build

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Tests on darwin-x64

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Tests on win32-x64-msvc

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Tests on win32-arm64-msvc

unused variable: `id`

Check failure on line 92 in src/query_plan/execution/executor.rs

View workflow job for this annotation

GitHub Actions / Run Tests on win32-x64-gnu

unused variable: `id`
todo!()
}
ExecutionStep::Sequential(steps) => {
for step in steps {
self.execute(step).await;
Expand Down
41 changes: 17 additions & 24 deletions src/query_plan/execution/simple.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,32 @@
use crate::query_plan::plan::{FieldTreeEntry, OperationPlan};
use crate::query_plan::plan::{FieldTree, FieldTreeEntry, OperationPlan};

Check warning on line 1 in src/query_plan/execution/simple.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/query_plan/execution/simple.rs

use super::execution::ExecutionStep;

pub struct SimpleExecutionBuilder {}

impl SimpleExecutionBuilder {
pub fn build(&self, operation_plan: &OperationPlan) -> ExecutionStep {
fn inner_build(&self, tree: &FieldTree) -> ExecutionStep {
let mut steps = Vec::new();
let mut queue = vec![&operation_plan.field_tree];

while !queue.is_empty() {
let mut new_queue = Vec::new();
let mut parallel_steps = Vec::new();

for tree in queue {
if let Some(field_plan_id) = &tree.field_plan_id {
parallel_steps.push(ExecutionStep::Resolve(*field_plan_id));
}

match &tree.entry {
FieldTreeEntry::Compound(children) | FieldTreeEntry::CompoundList(children) => {
for tree in children.values() {
new_queue.push(tree);
}
}
_ => {}
match &tree.entry {
FieldTreeEntry::Compound(children) | FieldTreeEntry::CompoundList(children) => {
for tree in children.values() {
steps.push(self.inner_build(tree));
}
}
_ => {}
}

if !parallel_steps.is_empty() {
steps.push(ExecutionStep::parallel(parallel_steps));
}
queue = new_queue;
let steps = ExecutionStep::Parallel(steps);

if let Some(field_plan_id) = &tree.field_plan_id {
ExecutionStep::Sequential(vec![ExecutionStep::Resolve(*field_plan_id), steps])
} else {
steps
}
}

ExecutionStep::sequential(steps)
pub fn build(&self, operation_plan: &OperationPlan) -> ExecutionStep {
self.inner_build(&operation_plan.field_tree).flatten()
}
}
2 changes: 1 addition & 1 deletion src/query_plan/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use async_graphql::{
use indenter::indented;

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Id(usize);
pub struct Id(pub usize);

impl Display for Id {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
source: src/query_plan/tests.rs
expression: execution_plan
---
Sequential:
Parallel:
Parallel:
Sequential:
Resolve(3)
Resolve(6)
Resolve(4)
Resolve(4)
Resolve(6)
2 changes: 1 addition & 1 deletion src/query_plan/tests/user-posts.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type User {
username: String!
email: String!
phone: String
website: String @const(data: "/users/website/{{value.username}}")
website: String @expr(body: "/users/website/{{value.username}}")
}

type Post {
Expand Down

0 comments on commit 3c7f1ec

Please sign in to comment.