You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This change adds support for the concurrent resolution of lazy
objects. While it is currently possible to return something like
a `::Concurrent::Promise.execute` from a GraphQL method, there is
currently no way to make this work in tandem with lazy objects. Consider
the case in which we would like to use a Gem like `graphql-batch` (or
a thread-safe alternative) but execute queries in parallel whenever
possible:
```
{
post(id: 10) {
author { name }
comments { count }
}
}
```
In this query you could imagine that the each of `post`, `author`, and
`comments` are separate DB calls. While it may not be possible for
`post` and `author` to be executed in parallel, certainly `author` and
`comments` can. But we would still like to perform this operation lazily
because if this query is expanded:
```
{
a: post(id: 10) {
author { name }
comments { count }
}
b: post(id: 11) {
author { name }
comments { count }
}
}
```
We would like to be able to load the authors for post 10 and 11 in a
single query.
I have implemented this solution by allowing the `lazy_resolve`
directive to accept an additional method name (the concurrent execution
method). This method will be called on all layers (breadth first) before
the `value` method is called. This ensures that concurrent execution can
be delayed until the last possible moment (to enable batching) but it
also ensures that multiple batches can be run in parallel if they are
resolved in the same graph layer.
Although intended for concurrent execution, it is not necessary for
this new method to actually perform an operation concurrently (i.e it
does not need to return a Thread or anything like that). This allows
`graphql-ruby` to not enforce any specific parallel execution primitive
(threads or `concurrent-ruby` could be used interchangeably).
I know this is a large PR, so I am happy to split it up into multiple
PRs if the overall approach is agreeable.
@@ -604,14 +606,26 @@ class InvalidDocumentError < Error; end;
604
606
605
607
# @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered wtih {#lazy_resolve}.
606
608
deflazy_method_name(obj)
607
-
@lazy_methods.get(obj)
609
+
spec=@lazy_methods.get(obj)
610
+
spec && spec.value_method
611
+
end
612
+
613
+
# @return [Symbol, nil] The method name to concurrently resolve `obj`, or nil if `obj`'s class wasn't registered wtih {#lazy_resolve} with a concurrent method.
614
+
defconcurrent_method_name(obj)
615
+
spec=@lazy_methods.get(obj)
616
+
spec && spec.exec_method
608
617
end
609
618
610
619
# @return [Boolean] True if this object should be lazily resolved
611
620
deflazy?(obj)
612
621
!!lazy_method_name(obj)
613
622
end
614
623
624
+
# @return [Boolean] True if this object should be concurrently executed
0 commit comments