Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
0ba108b
- DependencyGraph initial implementation
gkesler Dec 29, 2018
17f6a30
- simplified Vertex definition not to rely on a DependencyGraph
gkesler Dec 29, 2018
77a20a2
- refactored DependencyGraph.edges set to be backed by its vertices m…
gkesler Dec 29, 2018
61ae699
- further simplified implementation, added Edge.connectEndpoints and …
gkesler Dec 30, 2018
73cfcbc
- some code cleanup
gkesler Dec 30, 2018
4a4340d
- removed redundant check when calculating next closure of vertices t…
gkesler Dec 30, 2018
931b7fe
- added accessor to Edge.action property
gkesler Dec 31, 2018
1788694
- initial implementation of ExecuitonPlanBuilder
gkesler Jan 9, 2019
700b2ac
- saving current state
gkesler Jan 10, 2019
231d4fb
- saving current state
gkesler Jan 10, 2019
c6d8f04
- saving current state
gkesler Jan 11, 2019
5a43f28
- added computeXXXIfAbsent methods to TraverserContext to search recu…
gkesler Jan 11, 2019
f00e617
- code cleanu
gkesler Jan 11, 2019
8a918f6
- added TriXXX functors
gkesler Jan 11, 2019
9b98db9
- saving current state
gkesler Jan 11, 2019
15a3204
- updated traverse managers to allow custom implementations of Traver…
gkesler Jan 11, 2019
b26d5f4
- refactored traversal in ExecutionPlanBuilder to use custom Traverse…
gkesler Jan 11, 2019
6f7cd5e
- saving current state
gkesler Jan 11, 2019
5d06db0
- removed newly introduced unused funators
gkesler Jan 11, 2019
48f4d5d
eaned up ExecutionPlanBuilder implementation
gkesler Jan 11, 2019
60ae13b
- saving current state
gkesler Jan 12, 2019
e74310a
- code cleanup
gkesler Jan 12, 2019
ef8d638
- some code/expressions simplifications
gkesler Jan 14, 2019
882b99d
- saving current state
gkesler Jan 14, 2019
4125068
- Created ExecutionPlan class as a subclass of DependencyGraph and mo…
gkesler Jan 15, 2019
c2a94c0
- introduced DocumentVertex that passes its [root] data to the FieldV…
gkesler Jan 15, 2019
5790160
- added functional interfaces
gkesler Jan 16, 2019
d96e7f1
- added @FunctionalInterface annotation to TernaryOPerator
gkesler Jan 16, 2019
1fb8f7f
- added DependencyGraphContext interface
gkesler Jan 17, 2019
03e052d
- saving current state
gkesler Jan 17, 2019
771dbc8
- modified Vertex.dependsOn signature to allow brroader range of BiCo…
gkesler Jan 17, 2019
db43359
- simplified Vertex resolution protocol. Got read of canResolve method
gkesler Jan 17, 2019
f7683f7
- saving current state
gkesler Jan 17, 2019
73e7611
- saving current state
gkesler Jan 20, 2019
3425c9e
- Improved Async performance
gkesler Jan 24, 2019
8ddb8fe
- saving current state
gkesler Jan 25, 2019
23b8aeb
- saving current state
gkesler Jan 25, 2019
e53d011
- saving current state
gkesler Jan 26, 2019
40b2a80
- cleaned up propagation of the result holder across non-Field vertices
gkesler Jan 26, 2019
2f71c45
- implemented DAGExecutionStrategy based on DependencyGraph evaluation
gkesler Jan 26, 2019
f8c024f
- code cleanup
gkesler Jan 26, 2019
3fe624b
- overriden ExecutionPlan.orderDependecies method to create thread-sa…
gkesler Jan 26, 2019
83b9e8e
- synchronization optimization in EntityPlan.IteratorImpl
gkesler Jan 26, 2019
4af8a2f
- code cleanup
gkesler Jan 27, 2019
be7dad8
- simplifications and code cleanup
gkesler Jan 28, 2019
48cd644
- code cleanup
gkesler Jan 29, 2019
7eabd64
- code cleanup
gkesler Jan 29, 2019
48a1eb2
Merge branch 'master' into gkesler-dependency-graph
gkesler Jan 30, 2019
cb3f38f
- fixed formatting to reduce number of really changed files
gkesler Jan 30, 2019
92f5071
- corrected DependencyGraph traverser to automatically excluide unres…
gkesler Jan 30, 2019
32da077
Merge branch 'master' into gkesler-dependency-graph
gkesler Feb 21, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 21 additions & 32 deletions 53 src/main/java/graphql/execution/Async.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
import graphql.Internal;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

@Internal
@SuppressWarnings("FutureReturnValueIgnored")
Expand All @@ -24,41 +27,27 @@ public interface CFFactory<T, U> {
}

public static <U> CompletableFuture<List<U>> each(List<CompletableFuture<U>> futures) {
CompletableFuture<List<U>> overallResult = new CompletableFuture<>();

CompletableFuture
.allOf(futures.toArray(new CompletableFuture[0]))
.whenComplete((noUsed, exception) -> {
if (exception != null) {
overallResult.completeExceptionally(exception);
return;
}
List<U> results = new ArrayList<>();
for (CompletableFuture<U> future : futures) {
results.add(future.join());
}
overallResult.complete(results);
});
return overallResult;
Assert.assertNotNull(futures);

return CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[futures.size()]))
.thenApply(noUsed -> futures
.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}

public static <T, U> CompletableFuture<List<U>> each(Iterable<T> list, BiFunction<T, Integer, CompletableFuture<U>> cfFactory) {
List<CompletableFuture<U>> futures = new ArrayList<>();
int index = 0;
for (T t : list) {
CompletableFuture<U> cf;
try {
cf = cfFactory.apply(t, index++);
Assert.assertNotNull(cf, "cfFactory must return a non null value");
} catch (Exception e) {
cf = new CompletableFuture<>();
// Async.each makes sure that it is not a CompletionException inside a CompletionException
cf.completeExceptionally(new CompletionException(e));
}
futures.add(cf);
}
return each(futures);

Assert.assertNotNull(list);
Assert.assertNotNull(cfFactory);

int index[] = {0};
return each(
StreamSupport
.stream(list.spliterator(), false)
.map(o -> tryCatch(() ->
Assert.assertNotNull(cfFactory.apply(o, index[0]++), "cfFactory must return a non null value")))
.collect(Collectors.toList())
);
}

public static <T, U> CompletableFuture<List<U>> eachSequentially(Iterable<T> list, CFFactory<T, U> cfFactory) {
Expand Down
4 changes: 2 additions & 2 deletions 4 src/main/java/graphql/execution/FieldCollector.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ private String getFieldEntryKey(Field field) {
}


private boolean doesFragmentConditionMatch(FieldCollectorParameters parameters, InlineFragment inlineFragment) {
protected boolean doesFragmentConditionMatch(FieldCollectorParameters parameters, InlineFragment inlineFragment) {
if (inlineFragment.getTypeCondition() == null) {
return true;
}
Expand All @@ -129,7 +129,7 @@ private boolean doesFragmentConditionMatch(FieldCollectorParameters parameters,
return checkTypeCondition(parameters, conditionType);
}

private boolean doesFragmentConditionMatch(FieldCollectorParameters parameters, FragmentDefinition fragmentDefinition) {
protected boolean doesFragmentConditionMatch(FieldCollectorParameters parameters, FragmentDefinition fragmentDefinition) {
GraphQLType conditionType;
conditionType = getTypeFromAST(parameters.getGraphQLSchema(), fragmentDefinition.getTypeCondition());
return checkTypeCondition(parameters, conditionType);
Expand Down
234 changes: 234 additions & 0 deletions 234 src/main/java/graphql/execution3/DAGExecutionStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package graphql.execution3;

import static graphql.Assert.assertNotNull;
import graphql.ExecutionResult;
import graphql.ExecutionResultImpl;
import graphql.execution.Async;
import graphql.execution.ExecutionContext;
import graphql.execution.ExecutionPath;
import graphql.execution.ExecutionStepInfo;
import static graphql.execution.ExecutionStepInfo.newExecutionStepInfo;
import graphql.execution.ExecutionStepInfoFactory;
import graphql.execution.FetchedValue;
import graphql.execution.MergedField;
import graphql.execution.nextgen.ValueFetcher;
import graphql.language.Field;
import graphql.language.Node;
import graphql.schema.GraphQLOutputType;
import graphql.schema.GraphQLType;
import graphql.schema.GraphQLTypeUtil;
import graphql.util.DependenciesIterator;
import graphql.util.Edge;
import graphql.util.TriFunction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
*
* @author gkesler
*/
public class DAGExecutionStrategy implements ExecutionStrategy {
public DAGExecutionStrategy (ExecutionContext executionContext) {
this.executionContext = Objects.requireNonNull(executionContext);
this.executionInfoFactory = new ExecutionStepInfoFactory();
this.valueFetcher = new ValueFetcher();
}

/**
* Executes a graphql request according to the schedule
* provided by executionPlan
*
* @param executionPlan a {@code graphql.util.DependencyGraph} specialization that provides
* order of field resolution requests
*
* @return a CompletableFuture holding the result of execution.
*/
@Override
public CompletableFuture<ExecutionResult> execute(ExecutionPlan executionPlan) {
assertNotNull(executionPlan);

ExecutionPlanContextImpl executionPlanContext = new ExecutionPlanContextImpl();
return CompletableFuture
.completedFuture(executionPlan.orderDependencies(executionPlanContext))
.thenCompose(this::resolveClosure)
.thenApply(noResult -> executionPlanContext.getResult());
}

private CompletableFuture<DependenciesIterator<NodeVertex<Node, GraphQLType>>> resolveClosure (DependenciesIterator<NodeVertex<Node, GraphQLType>> closure) {
if (closure.hasNext()) {
Collection<NodeVertex<Node, GraphQLType>> nodes = closure.next();
List<CompletableFuture<Void>> tasks = new ArrayList<>(nodes.size());

Iterator<NodeVertex<Node, GraphQLType>> toBeResolved = nodes.iterator();
while (toBeResolved.hasNext()) {
NodeVertex<Node, GraphQLType> field = toBeResolved.next();
toBeResolved.remove();

tasks.add(
CompletableFuture
.completedFuture(field)
.thenCompose(this::fetchNode)
.thenAccept(closure::close)
);
}

// execute fetch&resove asynchronously
return Async
.each(tasks)
.thenApply(noResult -> closure)
.thenCompose(this::resolveClosure);
} else {
return CompletableFuture
.completedFuture(closure);
}
}

private void provideSource (NodeVertex<Node, GraphQLType> source, NodeVertex<Node, GraphQLType> sink) {
LOGGER.debug("provideSource: source={}, sink={}", source, sink);
source.accept(sink, new NodeVertexVisitor<NodeVertex<? extends Node, ? extends GraphQLType>>() {
@Override
public NodeVertex<? extends Node, ? extends GraphQLType> visit(OperationVertex source, NodeVertex<? extends Node, ? extends GraphQLType> sink) {
source
.executionStepInfo(
newExecutionStepInfo()
.type((GraphQLOutputType)source.getType())
.path(ExecutionPath.rootPath())
.build()
)
// prepare placeholder for this operation result
// since operation does not have its external reslution,
// it's result is stored in the source object
.result(source.getSource());

return visitNode(source, sink);
}

@Override
public NodeVertex<? extends Node, ? extends GraphQLType> visitNode(NodeVertex<? extends Node, ? extends GraphQLType> source, NodeVertex<? extends Node, ? extends GraphQLType> sink) {
return sink
.parentExecutionStepInfo(source.getExecutionStepInfo())
.source(Results.flatten((List<Object>)source.getResult()));
}
});
}

private void joinResults (FieldVertex source, NodeVertex<Node, GraphQLType> sink) {
LOGGER.debug("afterResolve: source={}, sink={}", source, sink);
Results.joinResultsOf(source);
}

private boolean tryResolve (NodeVertex<Node, GraphQLType> node) {
LOGGER.debug("tryResolve: node={}", node);
return node.accept(true, new NodeVertexVisitor<Boolean>() {
@Override
public Boolean visit(FieldVertex node, Boolean data) {
List<Object> sources = (List<Object>)node.getSource();
// if sources is empty, no need to fetch data.
// even if it is fetched, it won't be joined anyways
return sources == null || sources.isEmpty();
}

@Override
public Boolean visit(DocumentVertex node, Boolean data) {
node.result(result);
return true;
}
});
}

private CompletableFuture<NodeVertex<Node, GraphQLType>> fetchNode (NodeVertex<Node, GraphQLType> node) {
LOGGER.debug("fetchNode: node={}", node);

FieldVertex fieldNode = node.as(FieldVertex.class);
MergedField sameFields = MergedField
.newMergedField(fieldNode.getNode())
.build();
ExecutionStepInfo sourceExecutionStepInfo = node.getParentExecutionStepInfo();
GraphQLOutputType parentType = (GraphQLOutputType)GraphQLTypeUtil.unwrapAll(sourceExecutionStepInfo.getType());
ExecutionStepInfo parentExecutionStepInfo = sourceExecutionStepInfo
.transform(builder -> builder
.parentInfo(sourceExecutionStepInfo.getParent())
.type(parentType));
ExecutionStepInfo executionStepInfo = executionInfoFactory
.newExecutionStepInfoForSubField(executionContext, sameFields, parentExecutionStepInfo);

TriFunction<FieldVertex, MergedField, ExecutionStepInfo, CompletableFuture<List<FetchedValue>>> valuesFetcher =
fieldNode.isRoot() ? this::fetchRootValues : this::fetchBatchedValues;


return valuesFetcher.apply(fieldNode, sameFields, executionStepInfo)
.thenApply(fetchedValues ->
fetchedValues
.stream()
.map(FetchedValue::getFetchedValue)
.map(fv -> Results.checkAndFixNILs(fv, fieldNode))
.collect(Collectors.toList())
)
.thenApply(fetchedValues ->
(NodeVertex<Node, GraphQLType>)node
.executionStepInfo(executionStepInfo)
.result(fetchedValues)
);
}

private CompletableFuture<List<FetchedValue>> fetchRootValues (FieldVertex fieldNode, MergedField sameFields, ExecutionStepInfo executionStepInfo) {
return valueFetcher
.fetchValue(executionContext, executionContext.getRoot(), null/*toDoLocalContext*/, sameFields, executionStepInfo)
.thenApply(Collections::singletonList);
}

private CompletableFuture<List<FetchedValue>> fetchBatchedValues (FieldVertex fieldNode, MergedField sameFields, ExecutionStepInfo executionStepInfo) {
List<Object> sources = (List<Object>)fieldNode.getSource();
return valueFetcher
.fetchBatchedValues(executionContext, sources, sameFields,
Stream
.generate(() -> executionStepInfo)
.limit(sources.size())
.collect(Collectors.toList())
);
}

private class ExecutionPlanContextImpl implements ExecutionPlanContext {
@Override
public void prepareResolve(Edge<? extends NodeVertex<? extends Node, ? extends GraphQLType>, ?> edge) {
provideSource((NodeVertex<Node, GraphQLType>)edge.getSource(), (NodeVertex<Node, GraphQLType>)edge.getSink());
}

@Override
public void whenResolved(Edge<? extends NodeVertex<? extends Node, ? extends GraphQLType>, ?> edge) {
joinResults((FieldVertex)edge.getSource(), (NodeVertex<Node, GraphQLType>)edge.getSink());
}

@Override
public boolean resolve(NodeVertex<? extends Node, ? extends GraphQLType> node) {
return tryResolve((NodeVertex<Node, GraphQLType>)node);
}

public ExecutionResult getResult() {
return new ExecutionResultImpl(result.get(0), executionContext.getErrors());
}
};

private final ExecutionContext executionContext;
private final ExecutionStepInfoFactory executionInfoFactory;
private final ValueFetcher valueFetcher;
private final List<Object> result = Arrays.asList(new LinkedHashMap<String, Object>());

private static final Logger LOGGER = LoggerFactory.getLogger(DAGExecutionStrategy.class);
}
45 changes: 45 additions & 0 deletions 45 src/main/java/graphql/execution3/DocumentVertex.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package graphql.execution3;

import graphql.execution.ExecutionStepInfo;
import graphql.language.Document;
import graphql.schema.GraphQLType;
import java.util.Objects;

/**
* ExecutionPlan Vertex created around Document node
*/
public class DocumentVertex extends NodeVertex<Document, GraphQLType> {
public DocumentVertex(Document node) {
super(Objects.requireNonNull(node), null);
}

@Override
public DocumentVertex parentExecutionStepInfo(ExecutionStepInfo parentExecutionStepInfo) {
return (DocumentVertex)super.parentExecutionStepInfo(parentExecutionStepInfo);
}

@Override
public DocumentVertex executionStepInfo(ExecutionStepInfo value) {
return (DocumentVertex)super.executionStepInfo(value);
}

@Override
public DocumentVertex source(Object source) {
return (DocumentVertex)super.source(source);
}

@Override
public DocumentVertex result(Object result) {
return (DocumentVertex)super.result(result);
}

@Override
<U> U accept(U data, NodeVertexVisitor<? super U> visitor) {
return (U)visitor.visit(this, data);
}
}
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.