Skip to content

Commit 63dc972

Browse files
Merge pull request #140 from graph-quilt/defer-optimization
Defer optimization
2 parents 0f331ba + 9b3c1f9 commit 63dc972

18 files changed

+444
-208
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<modelVersion>4.0.0</modelVersion>
33
<groupId>com.intuit.graphql</groupId>
44
<artifactId>graphql-orchestrator-java</artifactId>
5-
<version>5.0.18-SNAPSHOT</version>
5+
<version>defer.0.0-SNAPSHOT</version>
66
<packaging>jar</packaging>
77
<name>graphql-orchestrator-java</name>
88
<description>GraphQL Orchestrator combines multiple graphql services into a single unified schema</description>

src/main/java/com/intuit/graphql/orchestrator/GraphQLOrchestrator.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.intuit.graphql.orchestrator;
22

3+
import com.intuit.graphql.orchestrator.deferDirective.DeferDirectiveInstrumentation;
34
import com.intuit.graphql.orchestrator.schema.RuntimeGraph;
45
import com.intuit.graphql.orchestrator.utils.MultipartUtil;
56
import graphql.ExecutionInput;
@@ -20,6 +21,7 @@
2021
import org.dataloader.DataLoader;
2122
import org.dataloader.DataLoaderRegistry;
2223
import reactor.core.publisher.Flux;
24+
import reactor.core.scheduler.Schedulers;
2325

2426
import java.util.Arrays;
2527
import java.util.LinkedList;
@@ -32,6 +34,7 @@
3234
import java.util.function.UnaryOperator;
3335
import java.util.stream.Collectors;
3436

37+
import static com.intuit.graphql.orchestrator.utils.DirectivesUtil.USE_DEFER;
3538
import static java.util.Objects.requireNonNull;
3639

3740
@Slf4j
@@ -92,6 +95,8 @@ public CompletableFuture<ExecutionResult> execute(ExecutionInput executionInput,
9295
if (newExecutionInput.getContext() instanceof GraphQLContext) {
9396
((GraphQLContext) newExecutionInput.getContext())
9497
.put(DATA_LOADER_REGISTRY_CONTEXT_KEY, newExecutionInput.getDataLoaderRegistry());
98+
((GraphQLContext) newExecutionInput.getContext())
99+
.put(USE_DEFER , false);
95100
}
96101
return graphQL.executeAsync(newExecutionInput);
97102
}
@@ -100,9 +105,11 @@ private CompletableFuture<ExecutionResult> executeWithDefer(ExecutionInput execu
100105
List<ExecutionInput> splitExecutionInputs = MultipartUtil.splitMultipartExecutionInput(executionInput).stream()
101106
.map(ei -> {
102107
DataLoaderRegistry registry = buildNewDataLoaderRegistry();
108+
103109
GraphQLContext graphqlContext = GraphQLContext.newContext()
104-
.of(executionInput.getGraphQLContext())
110+
.of((GraphQLContext)executionInput.getContext())
105111
.put(DATA_LOADER_REGISTRY_CONTEXT_KEY, registry)
112+
.put(USE_DEFER, true)
106113
.build();
107114
return ei.transform(builder -> {
108115
builder.dataLoaderRegistry(registry);
@@ -115,6 +122,7 @@ private CompletableFuture<ExecutionResult> executeWithDefer(ExecutionInput execu
115122
AtomicInteger responses = new AtomicInteger(0);
116123

117124
Flux<Object> executionResultPublisher = Flux.fromIterable(splitExecutionInputs)
125+
.publishOn(Schedulers.elastic())
118126
.map(constructGraphQL()::executeAsync)
119127
.map(CompletableFuture::join)
120128
.doOnNext(executionResult -> responses.getAndIncrement())
@@ -160,7 +168,7 @@ public static class Builder {
160168
private ExecutionStrategy queryExecutionStrategy = new AsyncExecutionStrategy();
161169
private ExecutionStrategy mutationExecutionStrategy = null;
162170
private List<Instrumentation> instrumentations = new LinkedList<>(
163-
Arrays.asList(new DataLoaderDispatcherInstrumentation()));
171+
Arrays.asList(new DataLoaderDispatcherInstrumentation(), new DeferDirectiveInstrumentation()));
164172

165173
private Builder() {
166174
}

src/main/java/com/intuit/graphql/orchestrator/batch/AuthDownstreamQueryModifier.java

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,5 @@
11
package com.intuit.graphql.orchestrator.batch;
22

3-
import static com.intuit.graphql.orchestrator.resolverdirective.FieldResolverDirectiveUtil.hasResolverDirective;
4-
import static com.intuit.graphql.orchestrator.utils.QueryPathUtils.getNodesAsPathList;
5-
import static com.intuit.graphql.orchestrator.utils.QueryPathUtils.pathListToFQN;
6-
import static com.intuit.graphql.orchestrator.utils.RenameDirectiveUtil.convertGraphqlFieldWithOriginalName;
7-
import static com.intuit.graphql.orchestrator.utils.RenameDirectiveUtil.getRenameKey;
8-
import static graphql.introspection.Introspection.TypeNameMetaFieldDef;
9-
import static graphql.schema.FieldCoordinates.coordinates;
10-
import static graphql.util.TreeTransformerUtil.changeNode;
11-
import static graphql.util.TreeTransformerUtil.deleteNode;
12-
import static java.util.Objects.nonNull;
13-
import static java.util.Objects.requireNonNull;
14-
153
import com.intuit.graphql.orchestrator.authorization.FieldAuthorization;
164
import com.intuit.graphql.orchestrator.authorization.FieldAuthorizationEnvironment;
175
import com.intuit.graphql.orchestrator.authorization.FieldAuthorizationResult;
@@ -24,6 +12,9 @@
2412
import com.intuit.graphql.orchestrator.utils.SelectionCollector;
2513
import graphql.GraphQLContext;
2614
import graphql.GraphqlErrorException;
15+
import graphql.language.Argument;
16+
import graphql.language.BooleanValue;
17+
import graphql.language.Directive;
2718
import graphql.language.Field;
2819
import graphql.language.FragmentDefinition;
2920
import graphql.language.InlineFragment;
@@ -40,17 +31,33 @@
4031
import graphql.schema.GraphQLUnionType;
4132
import graphql.util.TraversalControl;
4233
import graphql.util.TraverserContext;
34+
import lombok.Builder;
35+
import lombok.NonNull;
36+
import org.apache.commons.collections4.CollectionUtils;
37+
import org.apache.commons.collections4.MapUtils;
38+
4339
import java.util.ArrayList;
4440
import java.util.Collections;
4541
import java.util.List;
4642
import java.util.Map;
4743
import java.util.Objects;
4844
import java.util.Set;
4945
import java.util.stream.Collectors;
50-
import lombok.Builder;
51-
import lombok.NonNull;
52-
import org.apache.commons.collections4.CollectionUtils;
53-
import org.apache.commons.collections4.MapUtils;
46+
47+
import static com.intuit.graphql.orchestrator.resolverdirective.FieldResolverDirectiveUtil.hasResolverDirective;
48+
import static com.intuit.graphql.orchestrator.utils.DirectivesUtil.DEFER_DIRECTIVE_NAME;
49+
import static com.intuit.graphql.orchestrator.utils.DirectivesUtil.DEFER_IF_ARG;
50+
import static com.intuit.graphql.orchestrator.utils.DirectivesUtil.USE_DEFER;
51+
import static com.intuit.graphql.orchestrator.utils.QueryPathUtils.getNodesAsPathList;
52+
import static com.intuit.graphql.orchestrator.utils.QueryPathUtils.pathListToFQN;
53+
import static com.intuit.graphql.orchestrator.utils.RenameDirectiveUtil.convertGraphqlFieldWithOriginalName;
54+
import static com.intuit.graphql.orchestrator.utils.RenameDirectiveUtil.getRenameKey;
55+
import static graphql.introspection.Introspection.TypeNameMetaFieldDef;
56+
import static graphql.schema.FieldCoordinates.coordinates;
57+
import static graphql.util.TreeTransformerUtil.changeNode;
58+
import static graphql.util.TreeTransformerUtil.deleteNode;
59+
import static java.util.Objects.nonNull;
60+
import static java.util.Objects.requireNonNull;
5461

5562
/**
5663
* This class modifies for query for a downstream provider.
@@ -98,6 +105,22 @@ public TraversalControl visitField(Field node, TraverserContext<Node> context) {
98105
return deleteNode(context);
99106
}
100107

108+
if(!node.getDirectives(DEFER_DIRECTIVE_NAME).isEmpty()) {
109+
Argument deferArg = node.getDirectives(DEFER_DIRECTIVE_NAME).get(0).getArgument(DEFER_IF_ARG);
110+
if(graphQLContext.getOrDefault(USE_DEFER, false) && (deferArg == null || ((BooleanValue)deferArg.getValue()).isValue())) {
111+
decreaseParentSelectionSetCount(context.getParentContext());
112+
return deleteNode(context);
113+
} else {
114+
//remove directive from query since directive is not built in and will fail downstream if added
115+
List<Directive> directives = node.getDirectives()
116+
.stream()
117+
.filter(directive -> !DEFER_DIRECTIVE_NAME.equals(directive.getName()))
118+
.collect(Collectors.toList());
119+
120+
return changeNode(context, node.transform(builder -> builder.directives(directives)));
121+
}
122+
}
123+
101124
if(!serviceMetadata.getRenamedMetadata().getOriginalFieldNamesByRenamedName().isEmpty()) {
102125
String renamedKey = getRenameKey(null, node.getName(), true);
103126
String originalName = serviceMetadata.getRenamedMetadata().getOriginalFieldNamesByRenamedName().get(renamedKey);
@@ -125,6 +148,22 @@ public TraversalControl visitField(Field node, TraverserContext<Node> context) {
125148
}
126149
}
127150

151+
if(!node.getDirectives(DEFER_DIRECTIVE_NAME).isEmpty()) {
152+
Argument deferArg = node.getDirectives(DEFER_DIRECTIVE_NAME).get(0).getArgument(DEFER_IF_ARG);
153+
if(graphQLContext.getOrDefault(USE_DEFER, false) && (deferArg == null || ((BooleanValue)deferArg.getValue()).isValue())) {
154+
decreaseParentSelectionSetCount(context.getParentContext());
155+
return deleteNode(context);
156+
} else {
157+
//remove directive from query since directive is not built in and will fail downstream if added
158+
List<Directive> directives = node.getDirectives()
159+
.stream()
160+
.filter(directive -> !DEFER_DIRECTIVE_NAME.equals(directive.getName()))
161+
.collect(Collectors.toList());
162+
163+
return changeNode(context, node.transform(builder -> builder.directives(directives)));
164+
}
165+
}
166+
128167
String renameKey = getRenameKey(parentType.getName(), node.getName(), false);
129168
String originalName = serviceMetadata.getRenamedMetadata().getOriginalFieldNamesByRenamedName().get(renameKey);
130169

src/main/java/com/intuit/graphql/orchestrator/batch/DownstreamQueryModifier.java

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
package com.intuit.graphql.orchestrator.batch;
22

3-
import static com.intuit.graphql.orchestrator.resolverdirective.FieldResolverDirectiveUtil.hasResolverDirective;
4-
import static com.intuit.graphql.orchestrator.utils.RenameDirectiveUtil.convertGraphqlFieldWithOriginalName;
5-
import static com.intuit.graphql.orchestrator.utils.RenameDirectiveUtil.getRenameKey;
6-
import static graphql.introspection.Introspection.TypeNameMetaFieldDef;
7-
import static graphql.schema.FieldCoordinates.coordinates;
8-
import static graphql.util.TreeTransformerUtil.changeNode;
9-
import static graphql.util.TreeTransformerUtil.deleteNode;
10-
import static java.util.Objects.requireNonNull;
11-
123
import com.intuit.graphql.orchestrator.federation.RequiredFieldsCollector;
134
import com.intuit.graphql.orchestrator.federation.metadata.FederationMetadata;
145
import com.intuit.graphql.orchestrator.schema.ServiceMetadata;
156
import com.intuit.graphql.orchestrator.schema.transform.FieldResolverContext;
167
import com.intuit.graphql.orchestrator.utils.SelectionCollector;
8+
import graphql.GraphQLContext;
9+
import graphql.language.Argument;
10+
import graphql.language.BooleanValue;
11+
import graphql.language.Directive;
1712
import graphql.language.Field;
1813
import graphql.language.FragmentDefinition;
1914
import graphql.language.InlineFragment;
@@ -30,14 +25,27 @@
3025
import graphql.schema.GraphQLUnionType;
3126
import graphql.util.TraversalControl;
3227
import graphql.util.TraverserContext;
28+
import org.apache.commons.collections4.CollectionUtils;
29+
import org.apache.commons.collections4.MapUtils;
30+
3331
import java.util.Collections;
3432
import java.util.List;
3533
import java.util.Map;
3634
import java.util.Objects;
3735
import java.util.Set;
3836
import java.util.stream.Collectors;
39-
import org.apache.commons.collections4.CollectionUtils;
40-
import org.apache.commons.collections4.MapUtils;
37+
38+
import static com.intuit.graphql.orchestrator.resolverdirective.FieldResolverDirectiveUtil.hasResolverDirective;
39+
import static com.intuit.graphql.orchestrator.utils.DirectivesUtil.DEFER_DIRECTIVE_NAME;
40+
import static com.intuit.graphql.orchestrator.utils.DirectivesUtil.DEFER_IF_ARG;
41+
import static com.intuit.graphql.orchestrator.utils.DirectivesUtil.USE_DEFER;
42+
import static com.intuit.graphql.orchestrator.utils.RenameDirectiveUtil.convertGraphqlFieldWithOriginalName;
43+
import static com.intuit.graphql.orchestrator.utils.RenameDirectiveUtil.getRenameKey;
44+
import static graphql.introspection.Introspection.TypeNameMetaFieldDef;
45+
import static graphql.schema.FieldCoordinates.coordinates;
46+
import static graphql.util.TreeTransformerUtil.changeNode;
47+
import static graphql.util.TreeTransformerUtil.deleteNode;
48+
import static java.util.Objects.requireNonNull;
4149

4250
/**
4351
* This class modifies for query for a downstream provider.
@@ -59,18 +67,22 @@ public class DownstreamQueryModifier extends NodeVisitorStub {
5967
private final SelectionCollector selectionCollector;
6068
private final GraphQLSchema graphQLSchema;
6169

70+
private final GraphQLContext graphQLContext;
71+
6272
public DownstreamQueryModifier(
63-
GraphQLType rootType,
64-
ServiceMetadata serviceMetadata,
65-
Map<String, FragmentDefinition> fragmentsByName,
66-
GraphQLSchema graphQLSchema) {
73+
GraphQLType rootType,
74+
ServiceMetadata serviceMetadata,
75+
Map<String, FragmentDefinition> fragmentsByName,
76+
GraphQLSchema graphQLSchema,
77+
GraphQLContext context) {
6778
Objects.requireNonNull(rootType);
6879
Objects.requireNonNull(serviceMetadata);
6980
Objects.requireNonNull(fragmentsByName);
7081
this.rootType = rootType;
7182
this.serviceMetadata = serviceMetadata;
7283
this.selectionCollector = new SelectionCollector(fragmentsByName);
7384
this.graphQLSchema = graphQLSchema;
85+
this.graphQLContext = context;
7486
}
7587

7688
@Override
@@ -85,6 +97,21 @@ public TraversalControl visitField(Field node, TraverserContext<Node> context) {
8597
}
8698
}
8799

100+
if(!node.getDirectives(DEFER_DIRECTIVE_NAME).isEmpty()) {
101+
Argument deferArg = node.getDirectives(DEFER_DIRECTIVE_NAME).get(0).getArgument(DEFER_IF_ARG);
102+
if(graphQLContext.getOrDefault(USE_DEFER, false) && (deferArg == null || ((BooleanValue)deferArg.getValue()).isValue())) {
103+
return deleteNode(context);
104+
} else {
105+
//remove directive from query since directive is not built in and will fail downstream if added
106+
List<Directive> directives = node.getDirectives()
107+
.stream()
108+
.filter(directive -> !DEFER_DIRECTIVE_NAME.equals(directive.getName()))
109+
.collect(Collectors.toList());
110+
111+
return changeNode(context, node.transform(builder -> builder.directives(directives)));
112+
}
113+
}
114+
88115
return TraversalControl.CONTINUE;
89116
} else {
90117
GraphQLFieldsContainer parentType = context.getParentContext().getVar(GraphQLType.class);
@@ -112,6 +139,21 @@ public TraversalControl visitField(Field node, TraverserContext<Node> context) {
112139
return changeNode(context, convertGraphqlFieldWithOriginalName(node, originalName));
113140
}
114141
}
142+
143+
if(!node.getDirectives(DEFER_DIRECTIVE_NAME).isEmpty()) {
144+
Argument deferArg = node.getDirectives(DEFER_DIRECTIVE_NAME).get(0).getArgument(DEFER_IF_ARG);
145+
if(graphQLContext.getOrDefault(USE_DEFER, false) && (deferArg == null || ((BooleanValue)deferArg.getValue()).isValue())) {
146+
return deleteNode(context);
147+
} else {
148+
//remove directive from query since directive is not built in and will fail downstream if added
149+
List<Directive> directives = node.getDirectives()
150+
.stream()
151+
.filter(directive -> !DEFER_DIRECTIVE_NAME.equals(directive.getName()))
152+
.collect(Collectors.toList());
153+
154+
return changeNode(context, node.transform(builder -> builder.directives(directives)));
155+
}
156+
}
115157
return TraversalControl.CONTINUE;
116158
}
117159
}

src/main/java/com/intuit/graphql/orchestrator/batch/EntityFetcherBatchLoader.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
package com.intuit.graphql.orchestrator.batch;
22

3-
import static com.intuit.graphql.orchestrator.utils.GraphQLUtil.AST_TRANSFORMER;
4-
import static com.intuit.graphql.orchestrator.utils.GraphQLUtil.unwrapAll;
5-
import static com.intuit.graphql.orchestrator.utils.IntrospectionUtil.__typenameField;
6-
import static graphql.language.Field.newField;
7-
import static graphql.language.InlineFragment.newInlineFragment;
8-
93
import com.intuit.graphql.orchestrator.ServiceProvider;
104
import com.intuit.graphql.orchestrator.federation.EntityFetchingException;
115
import com.intuit.graphql.orchestrator.federation.EntityQuery;
@@ -22,15 +16,22 @@
2216
import graphql.schema.DataFetchingEnvironment;
2317
import graphql.schema.GraphQLType;
2418
import graphql.schema.GraphQLTypeUtil;
19+
import org.apache.commons.collections4.CollectionUtils;
20+
import org.dataloader.BatchLoader;
21+
2522
import java.util.ArrayList;
2623
import java.util.Collection;
2724
import java.util.HashMap;
2825
import java.util.List;
2926
import java.util.Map;
3027
import java.util.concurrent.CompletionStage;
3128
import java.util.stream.Collectors;
32-
import org.apache.commons.collections4.CollectionUtils;
33-
import org.dataloader.BatchLoader;
29+
30+
import static com.intuit.graphql.orchestrator.utils.GraphQLUtil.AST_TRANSFORMER;
31+
import static com.intuit.graphql.orchestrator.utils.GraphQLUtil.unwrapAll;
32+
import static com.intuit.graphql.orchestrator.utils.IntrospectionUtil.__typenameField;
33+
import static graphql.language.Field.newField;
34+
import static graphql.language.InlineFragment.newInlineFragment;
3435

3536
public class EntityFetcherBatchLoader implements BatchLoader<DataFetchingEnvironment, DataFetcherResult<Object>> {
3637

@@ -116,7 +117,9 @@ private InlineFragment createEntityRequestInlineFragment(DataFetchingEnvironment
116117
if (!GraphQLTypeUtil.isLeaf(fieldType)) {
117118
final Field transformedField = (Field) AST_TRANSFORMER.transform(originalField,
118119
new DownstreamQueryModifier(fieldType, entityServiceMetadata,
119-
dfe.getFragmentsByName(), dfe.getGraphQLSchema()));
120+
dfe.getFragmentsByName(), dfe.getGraphQLSchema(), dfe.getContext()
121+
)
122+
);
120123

121124
// is an object
122125
fieldSelectionSet =

0 commit comments

Comments
 (0)