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

Concurrently resolve fields #132

Closed

Conversation

sogko
Copy link
Member

@sogko sogko commented Jun 1, 2016

This PR addresses #106 and probably #119.
This also depends on PR #123 and has updated with changes from that PR.

Relevant commits specifically for this PR:
(The other commits are not related to this PR)


09574d6 Ensure rw-safety of ExecutionContext.errors which will be read/written by multiple routines/threads later

Uses sync.RWMutex to ensure safety of ExecutionContext.errors.

If the executor encounters errors while resolving fields (e.g. from recovering from panic or when ResolveFn returns an error), it will collect it and add to a list of errors, which will then be returned in graphql.Result.

Since this will be done concurrently if we use go-routines to resolve fields, this has to be thread-safe.


ab3b083 Experimental implementation of concurrently resolving fields

This change is contained in executeFields(...).

For each field to be resolved, create a go-routine to resolve a field. sync.WaitGroup is used to wait for all fields to complete.
This will happen for each field that has sub-fields as well.

Given the following query:

query Bar {
  article {
    id
    author {
      id
      name
    }
  }
  feed {
    title
    body
    author {
      id
      name
    }
  }
}
  Bar ---> article ---> id
      |           |
      |            --> author ---> id
      |                      |
      |                       ---> name
      ---> feed ---> title
                |
                ---> body
                |
                ---> author ---> id
                            |
                            ---> name
  1. article and feed will be resolved concurrently.
  2. Within article: id and author will be resolved concurrently.
  3. Within article.author: id and name will be resolved concurrently.
  4. Within feed: title, body and author will be resolved concurrently.
  5. Within feed.author: id and name will be resolved concurrently.

Cheers!

sogko added 3 commits May 10, 2016 14:41
* sogko/0.5.0: (32 commits)
  RFC: Return type overlap validation
  Tests for `NewDirectives()` for better coverage
  Clean up redundant code in `completeValueCatchingError` - `completeValue` would already have resolved the value for `func() interface{}`
  Add tests for type comparators
  Spec compliant @skip/@include.
  Fix bug where @include directive is ignored if @Skip is present.
  Test that `executor` threads `RootValue` correctly - Similar to `context` test
  Improve coercion error messages
  [RFC] Add explicit context arg to graphql execution
  Add GraphQLSchema types field
  Removed logs
  Follow up to: Move getTypeOf to executor and rename to defaultResolveTypeFn to mirror defaultResolveFn
  Add tests for default resolve function.
  Restored deprecated fields in `directives` in introspective query for coverage. Should be removed once `onOperation`, `onFragment`, `onField` are dropped from spec.
  Move getTypeOf to execute.js and rename to defaultResolveTypeFn to mirror defaultResolveFn
  Remove unused function parameters
  Remove unused function parameters
  Minor follow-up to graphql-go#311
  [RFC] Add Schema Definition to IDL.
  Updating schema parser to more closely match current state of RFC
  ...
@sogko sogko added this to the 0.5.0 milestone Jun 1, 2016
@sogko sogko mentioned this pull request Jun 1, 2016
sogko added 24 commits June 1, 2016 14:03
Commit:
6e736bf4733ada6aa15943c0529ed126e8dd8ef1 [6e736bf]
Parents:
86db7340be
Author:
Lee Byron <lee@leebyron.com>
Date:
8 April 2016 at 9:51:04 AM SGT
Labels:
HEAD
Commit:
cbded829525a68b9e0dab61621992cbf01348981 [cbded82]
Parents:
dd02973028
Author:
Zhaojun Zhang <zhaojun@coursera.org>
Date:
26 April 2016 at 2:04:47 AM SGT
Committer:
Lee Byron <lee@leebyron.com>
graphql/graphql-js#364

Commit:
0b72e7038761bec9fb5319cc08ea93ff8b071a9c [0b72e70]
Parents:
cbded82952
Author:
Mike Solomon <mike@mindsnacks.com>
Date:
26 April 2016 at 2:37:25 AM SGT
Committer:
Lee Byron <lee@leebyron.com>
graphql/graphql-js#363

* Improve validation error message when field names conflict

* Remove extra hint and add unit test
…raphql-go#355)

* Add suggestionList to return strings based on how simular they are to the input

* Suggests valid fields in `FieldsOnCorrectType`

* Suggest argument names

* Suggested valid type names

* Fix flow and unit test

* addressed comments in PR: move file, update comment, filter out more options, remove redundant warning

* fix typos

* fix lint

Commit:
5bc1b2541d1b5767de4016e10ae77021f81310fc [5bc1b25]
Parents:
4b08c36e86
Author:
Yuzhi <yuzhi.z@gmail.com>
Date:
27 April 2016 at 2:48:45 AM SGT
Committer:
Lee Byron <lee@leebyron.com>
- `quoteStrings` already defined outside on package scope
Commit:
30a783a2a4f100cad0f31085284895bf51aa565a [30a783a]
Parents:
5bc1b2541d
Author:
Lee Byron <lee@leebyron.com>
Date:
27 April 2016 at 8:49:33 AM SGT
This fixes a bug where an empty "block" list could be skipped by the printer.

Commit:
ea5b241487c884edb561b12e0a92e947107bbfc1 [ea5b241]
Parents:
ec05b5404b
Author:
Lee Byron <lee@leebyron.com>
Date:
5 May 2016 at 7:26:11 AM SGT
Commit Date:
5 May 2016 at 7:26:13 AM SGT
Commit:
88cf354cd65e37fa0793600f6f2a3e6d1b29d21f [88cf354]
Parents:
2ac41f6f19
Author:
Lee Byron <lee@leebyron.com>
Date:
7 May 2016 at 3:09:16 AM SGT
Commit:
71b6a4aaecf064c7095bca81cc4285fa74ba175e [71b6a4a]
Parents:
980bdf403f
Author:
Lee Byron <lee@leebyron.com>
Date:
7 May 2016 at 4:26:20 AM SGT
This implements adding directives to the experimental schema language by extending the *locations* a directive can be used.

Notice that this provides no semantic meaning to these directives - they are purely a mechanism for annotating an AST - however future directives which contain semantic meaning may be introduced in the future (the first will be `@deprecated`).

Commit:
1b6824bc5df15f8edb259d535aa41a81e2a07234 [1b6824b]
Parents:
71b6a4aaec
Author:
Lee Byron <lee@leebyron.com>
Date:
7 May 2016 at 5:56:25 AM SGT
Labels:
HEAD
This allows directives to be defined on schema definitions.

Commit:
0aa78f61a2dc150b5ea9ee4f50b68a736796f068 [0aa78f6]
Parents:
1b6824bc5d
Author:
Lee Byron <lee@leebyron.com>
Date:
7 May 2016 at 7:05:27 AM SGT
Labels:
HEAD
This adds a new directive as part of the experimental schema language:

```
directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION | ENUM_VALUE
```

It also adds support for this directive in the schemaPrinter and buildASTSchema.

Additionally exports a new helper `specifiedDirectives` which is encoured to be used when addressing the collection of all directives defined by the spec. The `@deprecated` directive is optimistically added to this collection. While it's currently experimental, it will become part of the schema definition language RFC.

Commit:
5375c9b20452801b69dba208cac15d32e02ac608 [5375c9b]
Parents:
0aa78f61a2
Author:
Lee Byron <lee@leebyron.com>
Date:
9 May 2016 at 5:56:16 AM SGT
Labels:
HEAD
This adds a new directive as part of the experimental schema language:

```
directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION | ENUM_VALUE
```

It also adds support for this directive in the schemaPrinter and buildASTSchema.

Additionally exports a new helper `specifiedDirectives` which is encoured to be used when addressing the collection of all directives defined by the spec. The `@deprecated` directive is optimistically added to this collection. While it's currently experimental, it will become part of the schema definition language RFC.

Commit:
5375c9b20452801b69dba208cac15d32e02ac608 [5375c9b]
Parents:
0aa78f61a2
Author:
Lee Byron <lee@leebyron.com>
Date:
9 May 2016 at 5:56:16 AM SGT
Labels:
HEAD
…ogko/0.6.0

* 'sogko/0.6.0' of https://github.com/sogko/graphql:
  Revert back directives to use `var`; `const` not applicable here
  Deprecated directive (graphql-go#384)
The functions within this validator did not need to close over any state, which allows them to be pure functions.

Commit:
3a01fac4623b37eb7efcdece7a2c33ef74750aeb [3a01fac]
Parents:
5375c9b204
Author:
Lee Byron <lee@leebyron.com>
Date:
10 May 2016 at 6:12:08 AM SGT
Commit Date:
10 May 2016 at 6:12:11 AM SGT
Labels:
HEAD
Two more functions from the overlapping-fields validator which now accept arguments rather than closing over them locally.

Commit:
cf9be874228f4e16762b481d0abed5575f5587db [cf9be87]
Parents:
3a01fac462
Author:
Lee Byron <lee@leebyron.com>
Date:
10 May 2016 at 6:22:53 AM SGT
Commit Date:
10 May 2016 at 6:24:50 AM SGT
…r than fragment AST node

Commit:
688a1ee20db01ca80fdec2189298078380d81ed6 [688a1ee]
Parents:
cf9be87422
Author:
Lee Byron <lee@leebyron.com>
Date:
10 May 2016 at 9:54:10 AM SGT
@andreas
Copy link

andreas commented Jun 16, 2016

If we can make opt-in concurrency very easy, I think it could be a good solution. Considering the approach of returning a channel from a resolve function, a helper function could make it very easy and get rid of the boilerplate of listening to channels, re-panicking in the executor goroutine, etc.

Resolve: graphql.Parallel(func(params graphql.ResolveParams) (interface{}, error) {
  // ...
})

I think it's less obvious how to get easy/clean opt-in concurrency support for the elements of a list, since the resolve function is tied to the list itself, not the elements. Maybe add a field to List which indicates whether to evaluate the list elements concurrently or not? It doesn't feel as clean though.

It would be interesting to look into the overhead of the current approach, e.g. via the new benchmarks (#22). A pool of goroutines could potentially be used to decrease the overhead and limit the number of goroutines.

@bigdrum
Copy link
Contributor

bigdrum commented Jul 10, 2016

Even if the go routine overhead isn't a concern, I see another potential problem with this PR. The best feature I like for graphql is the possibility and simplicity of batching/deduping requests to the backend service (e.g. plugging it with facebook/dataloader), which brings substantial performance benefit. Doing parallel in this way makes it very hard (afaict) to implement such features.

If we allow the resolve function to return a channel as @Matthias247 suggested, or a func() (interface{}, error) that I personally prefer, the batching mechanism would be possible to implement (which allow some hook to collect all such promise-like results and dispatch batched requests).

The implementation of supporting such promise-like feature might be more complicated though, and I'm still trying to figure it out a way to do it.

@rantav
Copy link

rantav commented Jul 12, 2016

My vote goes to opt-in concurrency.
Would love to see this feature a reality.
My use case: Resolving fields from many database queries. Obviously this is something that can enjoy parallelism.

@andreas
Copy link

andreas commented Jul 21, 2016

Following this discussion, I've made a PR for opt-in parallel resolving of fields and lists: sogko#23

@alonrolnik
Copy link

alonrolnik commented Aug 3, 2016

I really would like to see this going forward, my use case is the same as @rantav, resolving fields from multiple DB queries which can be executed in parallel.
Any time estimation for it? is there any temporary solution that I can use in the meantime?
Thanks!

* master:
  gofmt -s
  executor: add graphql tag
  Fix ObjectConfig duplicate json tag. Name had a 'description' json tag which was duplicate
  Minor fix to stop `go vet` from complaining
  Add 1.7 and tip to build matrix for Go
  Minor fix to stop `go vet` from complaining
  README: add Documentation section
  Fixes tests that was broken with enhancements made to the `lexer` with commit graphql-go#137
  Improve lexer performance by using a byte array as source
  Updated `graphql.Float` coercion - if value type is explicitly `float32`, return `float32` - if value type is explicitly `float64`, return `float64` - if value type is `string`, return `float64` - for everything else, use system-default
* sogko/0.6.0: (34 commits)
  gofmt -s
  executor: add graphql tag
  Fix ObjectConfig duplicate json tag. Name had a 'description' json tag which was duplicate
  Minor fix to stop `go vet` from complaining
  Add 1.7 and tip to build matrix for Go
  Minor fix to stop `go vet` from complaining
  README: add Documentation section
  Fixes tests that was broken with enhancements made to the `lexer` with commit graphql-go#137
  Improve lexer performance by using a byte array as source
  Validation: improving overlapping fields quality (graphql-go#386)
  Validation: context.getFragmentSpreads now accepts selectionSet rather than fragment AST node
  Factor out more closure functions
  Factor out closure functions to normal functions
  Deprecated directive (graphql-go#384)
  Revert back directives to use `var`; `const` not applicable here
  Deprecated directive (graphql-go#384)
  RFC: Directive location: schema definition (graphql-go#382)
  RFC: Schema Language Directives (graphql-go#376)
  Update `NewSchema()` to use new exported introspective types
  Export introspection in public API
  ...
@coveralls
Copy link

coveralls commented Jan 27, 2017

Coverage Status

Coverage increased (+0.1%) to 83.604% when pulling 0ba9e8f on sogko:sogko/experiment-parallel-resolve into 22050ee on graphql-go:master.

@chris-ramon chris-ramon removed this from the v0.5.0 milestone Aug 6, 2017
@Kliton
Copy link

Kliton commented Sep 13, 2017

Any update?

@sandeepone
Copy link

Any Update?

@eduardogalbiati
Copy link

Any update?

@Kliton
Copy link

Kliton commented Oct 6, 2017

Up

@jekiapp
Copy link
Contributor

jekiapp commented Dec 20, 2017

I've been experimenting to give opt-in concurency for resolving fields myself. Since I really need it. Is there any concern about why we discontinue this feature like as @andreas did in sogko#23?

@dvic
Copy link
Contributor

dvic commented Jan 30, 2018

I resolved the conflicts for this branch and created PR sogko#25, but there are still many data races due to the fact that Schema is not safe for concurrent use.

@divoxx
Copy link

divoxx commented Jun 20, 2018

Is there a specific reason why this hasn't been finished/merged? What is pending? This makes it nearly impossible to be used in a large application with complex graphql structure and even moderate traffic.

If there is a clear definition of the rationale/direction and what's currently pending, someone else can pick it up. I'm willing to take a stab at it.

@appleboy
Copy link
Contributor

@chris-ramon Could you have time to take look at this?

@chris-ramon
Copy link
Member

chris-ramon commented Jul 20, 2018

@chris-ramon Could you have time to take look at this?

  • Actually this PR adds experimental support for concurrency and mentioned in the top description.

ab3b083 Experimental implementation of concurrently resolving fields

  1. Returning an async function: Resolve fields asyncronously #213
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
  return func() (interface{}, error) {
    // runs within a goroutine
  }, nil
},
  1. Returning a channel Another asynchronous resolution PR. But using JS-like promises instead of goroutines. #357
ch := make(graphql.ResolvePromise, 1)
// ...
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
  return graphql.ResolvePromise(ch), nil
}

An another approach PR against a fork:

  1. Opt-in concurrency: Opt-in parallel resolving of fields and lists sogko/graphql#23
Parallel: true,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
  // runs within a goroutine
},

Which one would match your use cases @appleboy

@chris-ramon
Copy link
Member

Thanks a lot guys! 👍 — Closing this one in favor of #388, you might want to take a look to a real-use case working example that shows the support for concurrent resolvers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet