Skip to content

Commit 2388c4a

Browse files
samuelAndalonSamuel Vazquez
and
Samuel Vazquez
authored
feat(batching): v6 synchronize executions when attempting to remove an entry (#2014)
### 📝 Description cherry pick #2013 --------- Co-authored-by: Samuel Vazquez <[email protected]>
1 parent 9eabdde commit 2388c4a

File tree

3 files changed

+34
-46
lines changed

3 files changed

+34
-46
lines changed

executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/extensions/CompletableFutureExtensions.kt

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,25 @@ fun <V> CompletableFuture<V>.dispatchIfNeeded(
3737
val dataLoaderRegistry = environment.dataLoaderRegistry as? KotlinDataLoaderRegistry ?: throw MissingKotlinDataLoaderRegistryException()
3838

3939
if (dataLoaderRegistry.dataLoadersInvokedOnDispatch()) {
40-
val cantContinueExecution = when {
40+
when {
4141
environment.graphQlContext.hasKey(ExecutionLevelDispatchedState::class) -> {
42-
environment
43-
.graphQlContext.get<ExecutionLevelDispatchedState>(ExecutionLevelDispatchedState::class)
44-
.allExecutionsDispatched(Level(environment.executionStepInfo.path.level))
42+
val cantContinueExecution =
43+
environment
44+
.graphQlContext.get<ExecutionLevelDispatchedState>(ExecutionLevelDispatchedState::class)
45+
.allExecutionsDispatched(Level(environment.executionStepInfo.path.level))
46+
if (cantContinueExecution) {
47+
dataLoaderRegistry.dispatchAll()
48+
}
4549
}
4650
environment.graphQlContext.hasKey(SyncExecutionExhaustedState::class) -> {
4751
environment
4852
.graphQlContext.get<SyncExecutionExhaustedState>(SyncExecutionExhaustedState::class)
49-
.allSyncExecutionsExhausted()
53+
.ifAllSyncExecutionsExhausted {
54+
dataLoaderRegistry.dispatchAll()
55+
}
5056
}
5157
else -> throw MissingInstrumentationStateException()
5258
}
53-
54-
if (cantContinueExecution) {
55-
dataLoaderRegistry.dispatchAll()
56-
}
5759
}
5860
return this
5961
}

executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/state/SyncExecutionExhaustedState.kt

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,6 @@ class SyncExecutionExhaustedState(
4646
private val totalExecutions: AtomicReference<Int> = AtomicReference(totalOperations)
4747
val executions = ConcurrentHashMap<ExecutionId, ExecutionBatchState>()
4848

49-
/**
50-
* Remove an [ExecutionBatchState] from the state in case operation does not qualify for starting an execution,
51-
* for example:
52-
* - parsing, validation errors
53-
* - persisted query errors
54-
* - an exception during execution was thrown
55-
*/
56-
private fun removeExecution(executionId: ExecutionId) {
57-
if (executions.containsKey(executionId)) {
58-
executions.remove(executionId)
59-
totalExecutions.set(totalExecutions.get() - 1)
60-
}
61-
}
62-
6349
/**
6450
* Create the [ExecutionBatchState] When a specific [ExecutionInput] starts his execution
6551
*
@@ -84,11 +70,12 @@ class SyncExecutionExhaustedState(
8470
override fun onCompleted(result: ExecutionResult?, t: Throwable?) {
8571
if ((result != null && result.errors.size > 0) || t != null) {
8672
if (executions.containsKey(parameters.executionInput.executionId)) {
87-
executions.remove(parameters.executionInput.executionId)
88-
totalExecutions.set(totalExecutions.get() - 1)
89-
val allSyncExecutionsExhausted = allSyncExecutionsExhausted()
90-
if (allSyncExecutionsExhausted) {
91-
onSyncExecutionExhausted(executions.keys().toList())
73+
synchronized(executions) {
74+
executions.remove(parameters.executionInput.executionId)
75+
totalExecutions.set(totalExecutions.get() - 1)
76+
}
77+
ifAllSyncExecutionsExhausted { executionIds ->
78+
onSyncExecutionExhausted(executionIds)
9279
}
9380
}
9481
}
@@ -147,9 +134,8 @@ class SyncExecutionExhaustedState(
147134
executionState
148135
}
149136

150-
val allSyncExecutionsExhausted = allSyncExecutionsExhausted()
151-
if (allSyncExecutionsExhausted) {
152-
onSyncExecutionExhausted(executions.keys().toList())
137+
ifAllSyncExecutionsExhausted { executionIds ->
138+
onSyncExecutionExhausted(executionIds)
153139
}
154140
}
155141
override fun onCompleted(result: Any?, t: Throwable?) {
@@ -158,26 +144,26 @@ class SyncExecutionExhaustedState(
158144
executionState
159145
}
160146

161-
val allSyncExecutionsExhausted = allSyncExecutionsExhausted()
162-
if (allSyncExecutionsExhausted) {
163-
onSyncExecutionExhausted(executions.keys().toList())
147+
ifAllSyncExecutionsExhausted { executionIds ->
148+
onSyncExecutionExhausted(executionIds)
164149
}
165150
}
166151
}
167152
}
168153

169154
/**
170-
* Provide the information about when all [ExecutionInput] sharing a [GraphQLContext] exhausted their execution
155+
* execute a given [predicate] when all [ExecutionInput] sharing a [GraphQLContext] exhausted their execution.
171156
* A Synchronous Execution is considered Exhausted when all [DataFetcher]s of all paths were executed up until
172157
* a scalar leaf or a [DataFetcher] that returns a [CompletableFuture]
173158
*/
174-
fun allSyncExecutionsExhausted(): Boolean = synchronized(executions) {
175-
val operationsToExecute = totalExecutions.get()
176-
when {
177-
executions.size < operationsToExecute || !dataLoaderRegistry.onDispatchFuturesHandled() -> false
178-
else -> {
179-
executions.values.all(ExecutionBatchState::isSyncExecutionExhausted)
159+
fun ifAllSyncExecutionsExhausted(predicate: (List<ExecutionId>) -> Unit) =
160+
synchronized(executions) {
161+
val operationsToExecute = totalExecutions.get()
162+
if (executions.size < operationsToExecute || !dataLoaderRegistry.onDispatchFuturesHandled())
163+
return@synchronized
164+
165+
if (executions.values.all(ExecutionBatchState::isSyncExecutionExhausted)) {
166+
predicate(executions.keys().toList())
180167
}
181168
}
182-
}
183169
}

executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/DataLoaderSyncExecutionExhaustedInstrumentationTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -616,9 +616,9 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest {
616616
fun `Instrumentation should not consider executions that thrown exceptions`() {
617617
val executions = listOf(
618618
ExecutionInput.newExecutionInput("query test1 { astronaut(id: 1) { id name } }").operationName("test1").build(),
619-
ExecutionInput.newExecutionInput("query test2 { astronaut(id: 2) { id name } }").operationName("test2").build(),
620-
ExecutionInput.newExecutionInput("query test3 { mission(id: 3) { id designation } }").operationName("test3").build(),
621-
ExecutionInput.newExecutionInput("query test4 { mission(id: 4) { designation } }").operationName("OPERATION_NOT_IN_DOCUMENT").build()
619+
ExecutionInput.newExecutionInput("query test2 { astronaut(id: 2) { id name } }").operationName("OPERATION_NOT_IN_DOCUMENT").build(),
620+
ExecutionInput.newExecutionInput("query test3 { mission(id: 3) { id designation } }").operationName("OPERATION_NOT_IN_DOCUMENT").build(),
621+
ExecutionInput.newExecutionInput("query test4 { mission(id: 4) { designation } }").operationName("test4").build()
622622
)
623623

624624
val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute(
@@ -633,7 +633,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest {
633633
val missionStatistics = kotlinDataLoaderRegistry.dataLoadersMap["MissionDataLoader"]?.statistics
634634

635635
assertEquals(1, astronautStatistics?.batchInvokeCount)
636-
assertEquals(2, astronautStatistics?.batchLoadCount)
636+
assertEquals(1, astronautStatistics?.batchLoadCount)
637637

638638
assertEquals(1, missionStatistics?.batchInvokeCount)
639639
assertEquals(1, missionStatistics?.batchLoadCount)

0 commit comments

Comments
 (0)