-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Track Engine sync code execution #3876
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds a counter to the ExecutionContext to track whether the engine is running. The key changes include:
- Introducing an AtomicInteger (isRunning) with corresponding running() and finished() methods in ExecutionContext.
- Inserting calls to executionContext.running() and executionContext.finished() across various execution strategy classes.
- Ensuring that the execution state is tracked consistently during asynchronous execution.
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/java/graphql/execution/SubscriptionExecutionStrategy.java | Inserts running/finished calls in the subscription strategy; includes an unnecessary semicolon that can be cleaned up. |
| src/main/java/graphql/execution/ExecutionStrategy.java | Adds running/finished calls to track execution state in object and field resolution flows. |
| src/main/java/graphql/execution/ExecutionContext.java | Introduces the AtomicInteger counter and its helper methods for tracking execution state. |
| src/main/java/graphql/execution/AsyncSerialExecutionStrategy.java | Adds running/finished calls around asynchronous field resolution. |
| src/main/java/graphql/execution/AsyncExecutionStrategy.java | Integrates running/finished calls to manage execution state during asynchronous execution. |
| src/main/java/graphql/execution/AbstractAsyncExecutionStrategy.java | Wraps up asynchronous result handling with running/finished calls. |
src/main/java/graphql/execution/SubscriptionExecutionStrategy.java
Outdated
Show resolved
Hide resolved
| @Override | ||
| public CompletableFuture<ExecutionResult> execute(ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws NonNullableFieldWasNullException { | ||
|
|
||
| executionContext.running(); |
Copilot
AI
Mar 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider wrapping the code following executionContext.running() in a try-finally block to guarantee that executionContext.finished() is always called, ensuring the counter remains balanced even if exceptions occur.
| executionStrategyCtx.onDispatched(); | ||
|
|
||
| futures.await().whenComplete((completeValueInfos, throwable) -> { | ||
| executionContext.running(); |
Copilot
AI
Mar 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The repeated pattern of invoking executionContext.running() followed by executionContext.finished() could benefit from a refactoring (e.g. using try-finally constructs) to avoid potential imbalances in the counter during asynchronous processing.
Test Results 313 files 313 suites 47s ⏱️ Results for commit 339f657. ♻️ This comment has been updated with latest results. |
…java Co-authored-by: Copilot <[email protected]>
| @@ -60,11 +62,13 @@ public CompletableFuture<ExecutionResult> execute(ExecutionContext executionCont | |||
| executionStrategyCtx.onDispatched(); | |||
|
|
|||
| futures.await().whenComplete((completeValueInfos, throwable) -> { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this missing a executionContext.finished() before the .await() ??
| @@ -75,17 +79,21 @@ public CompletableFuture<ExecutionResult> execute(ExecutionContext executionCont | |||
| dataLoaderDispatcherStrategy.executionStrategyOnFieldValuesInfo(completeValueInfos); | |||
| executionStrategyCtx.onFieldValuesInfo(completeValueInfos); | |||
| fieldValuesFutures.await().whenComplete(handleResultsConsumer); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
executionContext.finished() before the .await() ??
| executionContext.finished(); | ||
| return CompletableFuture.completedFuture(isNotSensible.get()); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
executionContext.finished() before the Async.eachSequentially ?
| Async.CombinedBuilder<Object> resultFutures = fieldValuesCombinedBuilder(completeValueInfos); | ||
| dataLoaderDispatcherStrategy.executeObjectOnFieldValuesInfo(completeValueInfos, parameters); | ||
| resolveObjectCtx.onFieldValuesInfo(completeValueInfos); | ||
| resultFutures.await().whenComplete(handleResultsConsumer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.await() - should this have a executionContext.finished() before it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imagine this
instead of
executionContext.finished();
resultFutures.await().whenComplete(handleResultsConsumer);
we have helper method in the engine code
awaiting(executionContext, resultFutures.await())
and in that helper method it can
- call .finished()
- know when the CF returns and turn itself back on
| @@ -69,19 +69,24 @@ public CompletableFuture<ExecutionResult> execute(ExecutionContext executionCont | |||
| // | |||
| // when the upstream source event stream completes, subscribe to it and wire in our adapter | |||
| CompletableFuture<ExecutionResult> overallResult = sourceEventStream.thenApply((publisher) -> { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to turn this off here ??
| executionContext.running(); | ||
| Object publisher = fetchedValue.getFetchedValue(); | ||
| if (publisher != null) { | ||
| assertTrue(publisher instanceof Publisher, () -> "Your data fetcher must return a Publisher of events when using graphql subscriptions"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this threw an exception - we would still be running
| isRunning.incrementAndGet(); | ||
| try { | ||
| return callable.call(); | ||
| } catch (Exception e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why catch Exception - this seems wrong.
A GraphqlAbort exception for example will be wrapped in RuntimeException and its meaning lost
Ahh Callable throws exception - perhaps make a RuntimeCallable class instead
| try { | ||
| runnable.run(); | ||
| ; | ||
| } catch (Exception e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Runnable does not throw exceptions - so dont catch it
| } | ||
|
|
||
| @Nullable | ||
| EngineRunningObserver getEngineRunningObserver() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Internal
| import static graphql.collect.ImmutableKit.emptyList; | ||
|
|
||
| @PublicApi | ||
| @Internal |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Breaking change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep ... for 23
adding a counter to ExecutionContext that tracks if the engine is running
Contains a small breaking change: it makes the ExecutionContext builder non public. I think nobody should create ExecutionContexts outside GraphLQ Java.