Skip to content

Commit c080fa2

Browse files
committed
thread work
1 parent fd82595 commit c080fa2

File tree

1 file changed

+57
-18
lines changed

1 file changed

+57
-18
lines changed

content/blog/threads.md

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,36 +34,72 @@ as it is by far the most important).
3434

3535
# Blocking DataFetcher
3636

37-
So for example lets assume your `DataFetcher` access a DB (blocking, not reactive) and you decide
38-
to offload this work to a dedicated Thread pool:
37+
Lets assume your accessing a DB in blocking way in your `DataFetcher`:
3938

4039
{{< highlight Java "linenos=table" >}}
41-
CompletableFuture<String> get(DataFetchingEnvironment env) {
42-
return dbAccess.supplyAsync( getValueFromDb(env), dbThreadPool );
40+
String get(DataFetchingEnvironment env) {
41+
return getValueFromDb(env); // blocking the Thread until the value is read from DB
4342
};
4443
{{< / highlight >}}
4544
<p/>
4645

47-
The subsequent work done by GraphQL Java will be executed in the same `dbThreadPool` until it
48-
encounters a new `DataFetcher` returned by the user code and this new `CF` dedicates the Thread
49-
for the subsequent work.
46+
This is not completely wrong, but not recommend in general as the consequence of this kind of `DataFecher`
47+
is that GraphQL can't execute the query in the most efficient way.
5048

51-
In general offloading your work to a dedicated Thread pool is recommend if your `DataFetcher` is blocking,
52-
because otherwise GraphQL will not execute with maximal efficiency.
5349
For example for the following query:
5450

5551
{{< highlight Scala "linenos=table" >}}
5652
{
57-
field1
58-
field2
59-
field3
53+
dbData1
54+
dbData2
55+
dbData3
6056
}
6157
{{< / highlight >}}
6258
<p/>
63-
64-
GraphQL can invoke the `DataFetcher` for all three fields in parallel. But if your `DataFetcher` for
65-
`field1` is blocking GraphQL Java will also be blocked and only invoke the next `DataFetcher` once `field`
66-
is finished. Offloading your blocking code onto a separate Thread pool a shown above solves this problem.
59+
60+
If the `DataFetcher` for these `dbData` fields don't return a `CF`,
61+
but block the Thread until the data is read, GraphQL Java will not work with maximum efficiency.
62+
63+
GraphQL Java can invoke the `DataFetcher` for all three fields in parallel. But if your `DataFetcher` for
64+
`dbData1` is blocking GraphQL Java will also be blocked and only invoke the next `DataFetcher` once `dbData1`
65+
is finished.
66+
The recommend solution to this problem is offloading your blocking code onto a separate Thread pool
67+
as shown here:
68+
69+
{{< highlight Java "linenos=table" >}}
70+
CompletableFuture<String> get(DataFetchingEnvironment env) {
71+
return CompletableFuture.supplyAsync( getValueFromDb(env), dbThreadPool );
72+
};
73+
{{< / highlight >}}
74+
<p/>
75+
This code will maximize the performance and will cause all three fields to be fetched in parallel.
76+
77+
# Different pools for different work
78+
79+
The subsequent work done by GraphQL Java will be executed in the same `dbThreadPool` until it
80+
encounters a new `DataFetcher` returned by the user code and this new `CF` dedicates the Thread
81+
for the subsequent work.
82+
83+
If you want to have separate pools for different kind of work, one for the actual `DataFetcher` which normally
84+
involve IO and one of the actual GraphQL Java work (which is pure CPU), you need to switch back from your offloaded
85+
pool to a dedicated GraphQL Java pool before returning the `CF`. You can achieve this with code like this:
86+
87+
{{< highlight Java "linenos=table" >}}
88+
CompletableFuture<String> get(DataFetchingEnvironment env) {
89+
return CompletableFuture.supplyAsync( getValueFromDb(env), dbThreadPool )
90+
.handleAsync((result,exception) -> {
91+
if(exception !=null) throw exception;
92+
return result;
93+
}, graphqlJavaPool);
94+
};
95+
{{< / highlight >}}
96+
<p/>
97+
98+
Notice the `.handleAsync` which doesn't do anything except forwarding the result, but on a
99+
different pool (`graphqlJavaPool`).
100+
101+
This way you have different pools for different kind of work (one for CPU bound GraphQL Java work and one
102+
for multiple ones for IO bound work), which can be configured and monitored indepently.
67103

68104
# In a fully reactive system
69105
If your system is fully reactive your `DataFetcher` will more look like this
@@ -79,8 +115,11 @@ The code above could be implemented via [Async Http Client](https://github.com/A
79115
or [WebFlux WebClient](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-client).
80116
Both provide fully reactive HTTP clients.
81117

82-
Because the code is non blocking there is no need to offload anything on a dedicated Thread pool and
83-
GraphQL Java "automatically" works as efficiently as possible.
118+
Because the code is non blocking there is no need to offload anything on a dedicated Thread pool to avoid blocking
119+
GraphQL Java.
120+
121+
But you still might want to consider using a dedicated GraphQL Java pool as you otherwise use
122+
threads which are dedicated to IO.
84123

85124
# Feedback or questions
86125
We use [GitHub Discussions](https://github.com/graphql-java/graphql-java/discussions) for general feedback and questions.

0 commit comments

Comments
 (0)