Skip to content

Commit 59a5df5

Browse files
davidscottcohenandimarek
authored andcommitted
BatchedExecutionStrategy
1 parent 91cf8c0 commit 59a5df5

14 files changed

+1354
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,8 @@ See [specification](http://facebook.github.io/graphql/#sec-Normal-evaluation) fo
341341

342342
It's recommended to use a `ExecutorService` to speed up execution.
343343

344+
Alternatively, schemas with nested lists may benefit from using a BatchedExecutionStrategy and creating batched DataFetchers with get() methods annotated @Batched.
345+
344346
#### Logging
345347

346348
Logging is done with [SLF4J](http://www.slf4j.org/). Please have a look at the [Manual](http://www.slf4j.org/manual.html) for details.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package graphql.execution.batched;
2+
3+
import graphql.schema.DataFetchingEnvironment;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
9+
10+
/**
11+
* <p>
12+
* When placed on {@link graphql.schema.DataFetcher#get(DataFetchingEnvironment)}, indicates that this DataFetcher is batched.
13+
* This annotation must be used in conjunction with {@link BatchedExecutionStrategy}. Batching is valuable in many
14+
* situations, such as when a {@link graphql.schema.DataFetcher} must make a network or file system request.
15+
* </p>
16+
*
17+
* <p>
18+
* When a {@link graphql.schema.DataFetcher} is batched, the {@link DataFetchingEnvironment#getSource()} method is
19+
* guaranteed to return a {@link java.util.List}. The {@link graphql.schema.DataFetcher#get(DataFetchingEnvironment)}
20+
* method MUST return a parallel {@link java.util.List} which is equivalent to running a {@link graphql.schema.DataFetcher}
21+
* over each input element individually.
22+
* </p>
23+
*
24+
* <p>
25+
* Using the {@link Batched} annotation is equivalent to implementing {@link BatchedDataFetcher} instead of {@link graphql.schema.DataFetcher}.
26+
* It is preferred to use the {@link Batched} annotation.
27+
* </p>
28+
*
29+
* For example, the following two {@link graphql.schema.DataFetcher} objects are interchangeable if used with a
30+
* {@link BatchedExecutionStrategy}.
31+
* <pre>
32+
* {@code
33+
new DataFetcher() {
34+
@literal @Override
35+
@literal @Batched
36+
public Object get(DataFetchingEnvironment environment) {
37+
List<String> retVal = new ArrayList<>();
38+
for (String s: (List<String>) environment.getSource()) {
39+
retVal.add(s + environment.getArgument("text"));
40+
}
41+
return retVal;
42+
}
43+
}
44+
}
45+
* </pre>
46+
*
47+
* <pre>
48+
* {@code
49+
new DataFetcher() {
50+
@literal @Override
51+
public Object get(DataFetchingEnvironment e) {
52+
return ((String)e.getSource()) + e.getArgument("text");
53+
}
54+
}
55+
}
56+
* </pre>
57+
*
58+
*/
59+
@Target(ElementType.METHOD)
60+
@Retention(RetentionPolicy.RUNTIME)
61+
public @interface Batched {
62+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package graphql.execution.batched;
2+
3+
import graphql.schema.DataFetcher;
4+
5+
/**
6+
* See {@link Batched}.
7+
*/
8+
public interface BatchedDataFetcher extends DataFetcher {
9+
// Marker interface
10+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package graphql.execution.batched;
2+
3+
import graphql.schema.DataFetcher;
4+
import graphql.schema.DataFetchingEnvironment;
5+
import java.lang.reflect.Method;
6+
7+
/**
8+
*
9+
* Produces a BatchedDataFetcher for a given DataFetcher.
10+
* If that fetcher is already a BatchedDataFetcher we return it.
11+
* If that fetcher's get method is annotated @Batched then we delegate to it directly.
12+
* Otherwise we wrap the fetcher in a BatchedDataFetcher that iterates over the sources and invokes the delegate
13+
* on each source. Note that this forgoes any performance benefits of batching,
14+
* so regular DataFetchers should normally only be used if they are in-memory.
15+
*/
16+
17+
public class BatchedDataFetcherFactory {
18+
public BatchedDataFetcher create(final DataFetcher supplied) {
19+
if (supplied instanceof BatchedDataFetcher) {
20+
return (BatchedDataFetcher) supplied;
21+
}
22+
try {
23+
Method getMethod = supplied.getClass().getMethod("get", DataFetchingEnvironment.class);
24+
Batched batched = getMethod.getAnnotation(Batched.class);
25+
if (batched != null) {
26+
return new BatchedDataFetcher() {
27+
@Override
28+
public Object get(DataFetchingEnvironment environment) {
29+
return supplied.get(environment);
30+
}
31+
};
32+
}
33+
} catch (NoSuchMethodException e) {
34+
throw new IllegalArgumentException(e);
35+
}
36+
return new UnbatchingDataFetcher(supplied);
37+
}
38+
}

0 commit comments

Comments
 (0)