Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 18 additions & 9 deletions src/main/java/graphql/EngineRunningState.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

import static graphql.Assert.assertTrue;
import static graphql.execution.EngineRunningObserver.RunningState.NOT_RUNNING;
import static graphql.execution.EngineRunningObserver.RunningState.NOT_RUNNING_FINISH;
import static graphql.execution.EngineRunningObserver.RunningState.RUNNING;
import static graphql.execution.EngineRunningObserver.RunningState.RUNNING_START;

@Internal
public class EngineRunningState {
Expand All @@ -26,6 +28,9 @@ public class EngineRunningState {
@Nullable
private volatile ExecutionId executionId;

// if true the last decrementRunning() call will be ignored
private volatile boolean finished;

private final AtomicInteger isRunning = new AtomicInteger(0);

@VisibleForTesting
Expand Down Expand Up @@ -148,7 +153,7 @@ private void decrementRunning() {
return;
}
assertTrue(isRunning.get() > 0);
if (isRunning.decrementAndGet() == 0) {
if (isRunning.decrementAndGet() == 0 && !finished) {
changeOfState(NOT_RUNNING);
}
}
Expand Down Expand Up @@ -193,16 +198,20 @@ private void run(Runnable runnable) {
/**
* Only used once outside of this class: when the execution starts
*/
public <T> T call(Supplier<T> supplier) {
public CompletableFuture<ExecutionResult> engineRun(Supplier<CompletableFuture<ExecutionResult>> engineRun) {
if (engineRunningObserver == null) {
return supplier.get();
}
incrementRunning();
try {
return supplier.get();
} finally {
decrementRunning();
return engineRun.get();
}
isRunning.incrementAndGet();
changeOfState(RUNNING_START);

CompletableFuture<ExecutionResult> erCF = engineRun.get();
erCF = erCF.whenComplete((result, throwable) -> {
finished = true;
changeOfState(NOT_RUNNING_FINISH);
});
decrementRunning();
return erCF;
}


Expand Down
2 changes: 1 addition & 1 deletion src/main/java/graphql/GraphQL.java
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ public CompletableFuture<ExecutionResult> executeAsync(UnaryOperator<ExecutionIn
*/
public CompletableFuture<ExecutionResult> executeAsync(ExecutionInput executionInput) {
EngineRunningState engineRunningState = new EngineRunningState(executionInput);
return engineRunningState.call(() -> {
return engineRunningState.engineRun(() -> {
ExecutionInput executionInputWithId = ensureInputHasId(executionInput);
engineRunningState.updateExecutionId(executionInputWithId.getExecutionId());

Expand Down
8 changes: 8 additions & 0 deletions src/main/java/graphql/execution/EngineRunningObserver.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
public interface EngineRunningObserver {

enum RunningState {
/**
* Represents that the engine is running, for the first time
*/
RUNNING_START,
/**
* Represents that the engine code is actively running its own code
*/
Expand All @@ -22,6 +26,10 @@ enum RunningState {
* Represents that the engine code is asynchronously waiting for fetching to happen
*/
NOT_RUNNING,
/**
* Represents that the engine is finished
*/
NOT_RUNNING_FINISH
}


Expand Down
40 changes: 20 additions & 20 deletions src/test/groovy/graphql/EngineRunningTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import static graphql.ExecutionInput.newExecutionInput
import static graphql.execution.EngineRunningObserver.ENGINE_RUNNING_OBSERVER_KEY
import static graphql.execution.EngineRunningObserver.RunningState
import static graphql.execution.EngineRunningObserver.RunningState.NOT_RUNNING
import static graphql.execution.EngineRunningObserver.RunningState.NOT_RUNNING_FINISH
import static graphql.execution.EngineRunningObserver.RunningState.RUNNING
import static graphql.execution.EngineRunningObserver.RunningState.RUNNING_START

class EngineRunningTest extends Specification {

Expand Down Expand Up @@ -70,13 +72,13 @@ class EngineRunningTest extends Specification {
when:
def er = graphQL.executeAsync(ei)
then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING_START, NOT_RUNNING]

when:
states.clear()
cf.complete(new PreparsedDocumentEntry(document))
then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING, NOT_RUNNING_FINISH]
er.get().data == [hello: "world"]


Expand Down Expand Up @@ -114,13 +116,13 @@ class EngineRunningTest extends Specification {
when:
def er = graphQL.executeAsync(ei)
then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING_START, NOT_RUNNING]

when:
states.clear()
cf.complete(new InstrumentationState() {})
then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING, NOT_RUNNING_FINISH]
er.get().data == [hello: "world"]


Expand Down Expand Up @@ -158,14 +160,14 @@ class EngineRunningTest extends Specification {
when:
def er = graphQL.executeAsync(ei)
then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING_START, NOT_RUNNING]

when:
states.clear()
cf.complete(ExecutionResultImpl.newExecutionResult().data([hello: "world-modified"]).build())
then:
er.get().data == [hello: "world-modified"]
states == [RUNNING, NOT_RUNNING]
states == [RUNNING, NOT_RUNNING_FINISH]


}
Expand Down Expand Up @@ -195,7 +197,7 @@ class EngineRunningTest extends Specification {
def er = graphQL.execute(ei)
then:
er.data == [hello: "world"]
states == [RUNNING, NOT_RUNNING]
states == [RUNNING_START, NOT_RUNNING_FINISH]
}

def "multiple async DF"() {
Expand Down Expand Up @@ -251,7 +253,7 @@ class EngineRunningTest extends Specification {
when:
def er = graphQL.executeAsync(ei)
then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING_START, NOT_RUNNING]

when:
states.clear();
Expand All @@ -270,7 +272,7 @@ class EngineRunningTest extends Specification {
states.clear()
cf2.complete("world2")
then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING, NOT_RUNNING_FINISH]
er.get().data == [hello: "world", hello2: "world2", foo: [name: "FooName"], someStaticValue: [staticValue: "staticValue"]]
}

Expand Down Expand Up @@ -299,14 +301,14 @@ class EngineRunningTest extends Specification {
when:
def er = graphQL.executeAsync(ei)
then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING_START, NOT_RUNNING]

when:
states.clear();
cf.complete("world")

then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING, NOT_RUNNING_FINISH]
er.get().data == [hello: "world"]
}

Expand Down Expand Up @@ -334,15 +336,15 @@ class EngineRunningTest extends Specification {
when:
def er = graphQL.executeAsync(ei)
then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING_START, NOT_RUNNING]

when:
states.clear();
cf.complete("world")

then:
er.get().data == [hello: "world"]
states == [RUNNING, NOT_RUNNING]
states == [RUNNING, NOT_RUNNING_FINISH]
}


Expand Down Expand Up @@ -387,8 +389,7 @@ class EngineRunningTest extends Specification {

then:
result.errors.collect { it.message } == ["recovered"]
// we expect simply going from running to finshed
states == [RUNNING, NOT_RUNNING]
states == [RUNNING, NOT_RUNNING_FINISH]
}

def "async datafetcher failing with async exception handler"() {
Expand Down Expand Up @@ -429,7 +430,7 @@ class EngineRunningTest extends Specification {
def er = graphQL.executeAsync(ei)

then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING_START, NOT_RUNNING]

when:
states.clear()
Expand All @@ -445,8 +446,7 @@ class EngineRunningTest extends Specification {

then:
result.errors.collect { it.message } == ["recovered"]
// we expect simply going from running to finshed
new ArrayList<>(states) == [RUNNING, NOT_RUNNING]
states == [RUNNING, NOT_RUNNING_FINISH]
}


Expand Down Expand Up @@ -480,7 +480,7 @@ class EngineRunningTest extends Specification {
when:
def er = graphQL.executeAsync(ei)
then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING_START, NOT_RUNNING]

when:
states.clear();
Expand All @@ -494,7 +494,7 @@ class EngineRunningTest extends Specification {
cf2.complete("world2")

then:
states == [RUNNING, NOT_RUNNING]
states == [RUNNING, NOT_RUNNING_FINISH]
er.get().data == [hello: "world", hello2: "world2"]
}
}
Loading