Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query Execution Plans #677

Closed
michaelstaib opened this issue Apr 1, 2019 · 5 comments
Closed

Query Execution Plans #677

michaelstaib opened this issue Apr 1, 2019 · 5 comments
Assignees
Labels
Milestone

Comments

@michaelstaib
Copy link
Member

michaelstaib commented Apr 1, 2019

The current implementation of the execution engine executes field batches. Each field batch represents a query level. For features like @defer and stitching it would be better if we could provide the execution with an execution plan that defines how the query should be best executed. Instead of batches the query could process fields more like a stream where parts that are ready to be processed will pulled in as other fields are finished off.

@michaelstaib michaelstaib added 🎉 enhancement New feature or request design labels Apr 1, 2019
@michaelstaib michaelstaib added this to the 9.1.0 milestone May 20, 2019
@michaelstaib michaelstaib self-assigned this May 20, 2019
@michaelstaib michaelstaib pinned this issue May 20, 2019
@michaelstaib michaelstaib unpinned this issue Jun 24, 2019
@michaelstaib
Copy link
Member Author

michaelstaib commented Jun 24, 2019

Tasks:

  • Create Context
    Creates a resolver context

  • Execute Resolver
    Executes a single resolver

  • Execute In-Parallel
    Executes multiple tasks in parallel

  • Execute DataLoader Batch
    Executes multiple resolvers of the same DataLoader in one batch (prepare/dispatch).

@michaelstaib
Copy link
Member Author

michaelstaib commented Jul 1, 2019

public interface IExecutionTask
{
    Task InvokeAsync(IExecutionContext context);
}

public class ExecuteInParallel 
{
    public IReadOnlyList<IExecutionTask> Tasks { get; }

   ...
}

public class ExecuteResolver 
{
    public IResolverContext { get; }
    public FieldResolverDelegate Resolver { get; }

   ...
}

public interface IFieldInfo
{
    ObjectField Field { get; }
    FieldNode FieldSelection { get; }
    IReadOnlyList<...> Dependencies { get; }
}

@michaelstaib
Copy link
Member Author

michaelstaib commented Jul 2, 2019

Query Execution Plans

We are thinking about introducing query execution plans for quite a while now. We think we can benefit from execution plans in the following areas:

  • Schema Stitching
  • OData Stitching
  • Rest Stitching
  • DataLoader
  • Entity Framework

Our aim is that the execution plans are extendible so that one can write its own execution tasks that is able to optimize execution further or bring new capabilities to it.

Analysing Queries

In order to be able to optimize queries we need to know what a resolver does, and currently this can be challenging.

In code-first some resolvers are pretty good to analyse.

public async Task<Person> GetPersonAsync(string id, [DataLoader]IPersonDataLoader dataLoader)
{
  ...
}

Form the above example we know that the resolver takes the id argument and uses the IPersonDataLoader to retrieve a person from a DataLoader.

We could now pool these resolvers and execute all of them in one go.

What if we could even go further and give the analyzer more hints like the following:

type Query {
  person(id: ID! @field(type: "Person" field: "id")) : Person
}

In the above example we are now exposing that the argument passed into the resolver is actually the id of the person.

If a resolver depends on data of person an exoses that.

type Person {
  id: ID!
  name: String!
  bestFriend: Person! @dependsOn(type: "Person" field: "id")
  friends: [Person!]
}

The query engine could optimize the following request:

{
  person(id: "123")
  {
    name
    bestFriend {
      name
    }
  }
}

The optimized request could look like the following if written in graphQL:

{
  person(id: "123")
  {
    name
  }
  bestFriend(friendId: "123") {
    name
  }
}

So, we could actually fetch both information in parallel and then assemble results afterwards.

WORK IN PROGRESS

@michaelstaib michaelstaib modified the milestones: 9.1.0, 9.2.0 Jul 8, 2019
@michaelstaib michaelstaib changed the title Ordered Execution Query Execution Plans Sep 11, 2019
@michaelstaib
Copy link
Member Author

Just as a reminder for myself:

  • Handling of shared resources
  • Handling of pooled resources

@michaelstaib
Copy link
Member Author

This is implemented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
No open projects
Development

No branches or pull requests

1 participant