Skip to content

Commit 339f657

Browse files
committed
increase accuracy of engine running
1 parent 82c4ef3 commit 339f657

3 files changed

Lines changed: 87 additions & 15 deletions

File tree

src/main/java/graphql/execution/ExecutionContext.java

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Locale;
3030
import java.util.Map;
3131
import java.util.Set;
32+
import java.util.concurrent.CompletableFuture;
3233
import java.util.concurrent.atomic.AtomicInteger;
3334
import java.util.concurrent.atomic.AtomicReference;
3435
import java.util.function.Consumer;
@@ -370,31 +371,48 @@ public boolean isRunning() {
370371
return isRunning.get() > 0;
371372
}
372373

373-
public <T> T call(Supplier<T> callable) {
374+
public void incrementRunning() {
374375
if (isRunning.incrementAndGet() == 1 && engineRunningObserver != null) {
375376
engineRunningObserver.runningStateChanged(executionId, graphQLContext, true);
376377
}
378+
}
379+
380+
public void decrementRunning() {
381+
if (isRunning.decrementAndGet() == 0 && engineRunningObserver != null) {
382+
engineRunningObserver.runningStateChanged(executionId, graphQLContext, false);
383+
}
384+
}
385+
386+
public void incrementRunning(CompletableFuture<?> cf) {
387+
cf.whenComplete((result, throwable) -> {
388+
incrementRunning();
389+
});
390+
}
391+
392+
public void decrementRunning(CompletableFuture<?> cf) {
393+
cf.whenComplete((result, throwable) -> {
394+
decrementRunning();
395+
});
396+
397+
}
398+
399+
public <T> T call(Supplier<T> callable) {
400+
incrementRunning();
377401
try {
378402
return callable.get();
379403
} finally {
380-
if (isRunning.decrementAndGet() == 0 && engineRunningObserver != null) {
381-
engineRunningObserver.runningStateChanged(executionId, graphQLContext, false);
382-
}
404+
decrementRunning();
383405
}
384406
}
385407

386408
public void run(Runnable runnable) {
387-
if (isRunning.incrementAndGet() == 1 && engineRunningObserver != null) {
388-
engineRunningObserver.runningStateChanged(executionId, graphQLContext, true);
389-
}
409+
incrementRunning();
390410
try {
391411
runnable.run();
392412
} finally {
393-
if (isRunning.decrementAndGet() == 0 && engineRunningObserver != null) {
394-
engineRunningObserver.runningStateChanged(executionId, graphQLContext, false);
395-
}
396-
413+
decrementRunning();
397414
}
398415
}
399416

417+
400418
}

src/main/java/graphql/execution/ExecutionStrategy.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,8 @@ private Object fetchField(GraphQLFieldDefinition fieldDef, ExecutionContext exec
513513
if (fetchedObject instanceof CompletableFuture) {
514514
@SuppressWarnings("unchecked")
515515
CompletableFuture<Object> fetchedValue = (CompletableFuture<Object>) fetchedObject;
516-
return fetchedValue
516+
executionContext.decrementRunning(fetchedValue);
517+
CompletableFuture<FetchedValue> fetchedValueCF = fetchedValue
517518
.handle((result, exception) -> executionContext.call(() -> {
518519
fetchCtx.onCompleted(result, exception);
519520
if (exception != null) {
@@ -526,6 +527,8 @@ private Object fetchField(GraphQLFieldDefinition fieldDef, ExecutionContext exec
526527
}))
527528
.thenCompose(Function.identity())
528529
.thenApply(result -> unboxPossibleDataFetcherResult(executionContext, parameters, result));
530+
executionContext.incrementRunning(fetchedValue);
531+
return fetchedValueCF;
529532
} else {
530533
fetchCtx.onCompleted(fetchedObject, null);
531534
return unboxPossibleDataFetcherResult(executionContext, parameters, fetchedObject);

src/test/groovy/graphql/EngineRunningTest.groovy

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class EngineRunningTest extends Specification {
4545
states == [true, false]
4646
}
4747

48-
def "engine running state is observed with async datafetcher"() {
48+
def "engine running state is observed with one async datafetcher"() {
4949
given:
5050
def sdl = '''
5151
@@ -80,11 +80,62 @@ class EngineRunningTest extends Specification {
8080
cf.complete("world")
8181

8282
then:
83-
//TODO: why is that so much back and forth between true and false?
84-
states == [true, false, true, false, true, false, true, false]
83+
states == [true, false]
8584
er.get().data == [hello: "world"]
8685

8786
}
8887

88+
def "engine running state is observed with two async datafetcher"() {
89+
given:
90+
def sdl = '''
91+
92+
type Query {
93+
hello: String
94+
hello2: String
95+
}
96+
'''
97+
CompletableFuture cf1 = new CompletableFuture();
98+
CompletableFuture cf2 = new CompletableFuture();
99+
def df = { env ->
100+
return cf1;
101+
} as DataFetcher
102+
def df2 = { env ->
103+
return cf2
104+
} as DataFetcher
105+
106+
def fetchers = ["Query": ["hello": df, "hello2": df2]]
107+
def schema = TestUtil.schema(sdl, fetchers)
108+
def graphQL = GraphQL.newGraphQL(schema).build()
109+
110+
def query = "{ hello hello2 }"
111+
def ei = newExecutionInput(query).build()
112+
113+
List<Boolean> states = new CopyOnWriteArrayList<>();
114+
ei.getGraphQLContext().put(ENGINE_RUNNING_OBSERVER_KEY, {
115+
ExecutionId executionId, GraphQLContext context, boolean running ->
116+
states.add(running)
117+
} as EngineRunningObserver);
118+
119+
when:
120+
def er = graphQL.executeAsync(ei)
121+
then:
122+
states == [true, false]
123+
124+
when:
125+
states.clear();
126+
cf1.complete("world")
127+
128+
then:
129+
states == [true, false]
130+
131+
when:
132+
states.clear();
133+
cf2.complete("world2")
134+
135+
then:
136+
states == [true, false]
137+
er.get().data == [hello: "world", hello2: "world2"]
138+
}
139+
89140

90141
}

0 commit comments

Comments
 (0)