Skip to content

Commit b3795bb

Browse files
samuelAndalonSamuel Vazquez
andauthored
feat(batching): v7 check if execution was exhausted when there are errors (#2010)
### 📝 Description #2009 --------- Co-authored-by: Samuel Vazquez <[email protected]>
1 parent 3cf86d9 commit b3795bb

File tree

7 files changed

+118
-73
lines changed

7 files changed

+118
-73
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentation : AbstractSyncExecutionExh
4040
parameters: SyncExecutionExhaustedInstrumentationParameters
4141
): OnSyncExecutionExhaustedCallback = { _: List<ExecutionId> ->
4242
parameters
43-
.executionContext.executionInput
43+
.executionInput
4444
.dataLoaderRegistry
4545
.dispatchAll()
4646
}

executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/execution/AbstractSyncExecutionExhaustedInstrumentation.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,12 @@ abstract class AbstractSyncExecutionExhaustedInstrumentation : SimplePerformantI
5959
): InstrumentationContext<ExecutionResult>? =
6060
parameters.graphQLContext
6161
?.get<SyncExecutionExhaustedState>(SyncExecutionExhaustedState::class)
62-
?.beginExecution(parameters)
62+
?.beginExecution(
63+
parameters,
64+
this.getOnSyncExecutionExhaustedCallback(
65+
SyncExecutionExhaustedInstrumentationParameters(parameters.executionInput)
66+
)
67+
)
6368

6469
override fun beginExecutionStrategy(
6570
parameters: InstrumentationExecutionStrategyParameters,
@@ -78,7 +83,7 @@ abstract class AbstractSyncExecutionExhaustedInstrumentation : SimplePerformantI
7883
?.beginFieldFetch(
7984
parameters,
8085
this.getOnSyncExecutionExhaustedCallback(
81-
SyncExecutionExhaustedInstrumentationParameters(parameters.executionContext)
86+
SyncExecutionExhaustedInstrumentationParameters(parameters.executionContext.executionInput)
8287
)
8388
)
8489
}

executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/execution/SyncExecutionExhaustedInstrumentationParameters.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022 Expedia, Inc
2+
* Copyright 2024 Expedia, Inc
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,11 +17,11 @@
1717
package com.expediagroup.graphql.dataloader.instrumentation.syncexhaustion.execution
1818

1919
import com.expediagroup.graphql.dataloader.instrumentation.syncexhaustion.DataLoaderSyncExecutionExhaustedInstrumentation
20-
import graphql.execution.ExecutionContext
20+
import graphql.ExecutionInput
2121

2222
/**
2323
* Hold information that will be provided to an instance of [DataLoaderSyncExecutionExhaustedInstrumentation]
2424
*/
2525
data class SyncExecutionExhaustedInstrumentationParameters(
26-
val executionContext: ExecutionContext
26+
val executionInput: ExecutionInput
2727
)

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

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,36 +46,30 @@ 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
*
6652
* @param parameters contains information of which [ExecutionInput] will start his execution
6753
* @return a non null [InstrumentationContext] object
6854
*/
6955
fun beginExecution(
70-
parameters: InstrumentationExecutionParameters
56+
parameters: InstrumentationExecutionParameters,
57+
onSyncExecutionExhausted: OnSyncExecutionExhaustedCallback
7158
): InstrumentationContext<ExecutionResult> {
7259
executions.computeIfAbsent(parameters.executionInput.executionId) {
7360
ExecutionBatchState()
7461
}
7562
return object : SimpleInstrumentationContext<ExecutionResult>() {
7663
override fun onCompleted(result: ExecutionResult?, t: Throwable?) {
7764
if ((result != null && result.errors.size > 0) || t != null) {
78-
removeExecution(parameters.executionInput.executionId)
65+
if (executions.containsKey(parameters.executionInput.executionId)) {
66+
executions.remove(parameters.executionInput.executionId)
67+
totalExecutions.set(totalExecutions.get() - 1)
68+
val allSyncExecutionsExhausted = allSyncExecutionsExhausted()
69+
if (allSyncExecutionsExhausted) {
70+
onSyncExecutionExhausted(executions.keys().toList())
71+
}
72+
}
7973
}
8074
}
8175
}

executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/fixture/AstronautGraphQL.kt

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022 Expedia, Inc
2+
* Copyright 2024 Expedia, Inc
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -94,18 +94,16 @@ object AstronautGraphQL {
9494

9595
private val astronautService = AstronautService()
9696
private val astronautDataFetcher = DataFetcher { environment ->
97+
val astronautId = environment.getArgument<String>("id")?.toInt() ?: throw IllegalArgumentException("Astronaut ID is null")
9798
astronautService.getAstronaut(
98-
AstronautServiceRequest(
99-
environment.getArgument<String>("id").toInt()
100-
),
99+
AstronautServiceRequest(astronautId),
101100
environment
102101
)
103102
}
104103
private val createAstronautDataFetcher = DataFetcher { environment ->
104+
val astronautName = environment.getArgument<String>("name") ?: throw IllegalArgumentException("Astronaut name is null")
105105
astronautService.createAstronaut(
106-
CreateAstronautServiceRequest(
107-
environment.getArgument("name")
108-
)
106+
CreateAstronautServiceRequest(astronautName)
109107
)
110108
}
111109
private val astronautsDataFetcher = DataFetcher { environment ->
@@ -119,10 +117,9 @@ object AstronautGraphQL {
119117

120118
private val missionService = MissionService()
121119
private val missionDataFetcher = DataFetcher { environment ->
120+
val missionId = environment.getArgument<String>("id")?.toInt() ?: throw IllegalArgumentException("Mission ID is null")
122121
missionService.getMission(
123-
MissionServiceRequest(
124-
environment.getArgument<String>("id").toInt()
125-
),
122+
MissionServiceRequest(missionId),
126123
environment
127124
)
128125
}
@@ -135,26 +132,26 @@ object AstronautGraphQL {
135132
)
136133
}
137134
private val missionsByAstronautDataFetcher = DataFetcher { environment ->
138-
val astronaut = environment.getSource<Astronaut>()
135+
val astronautId = environment.getSource<Astronaut>()?.id ?: throw IllegalArgumentException("Astronaut ID is null")
139136
missionService
140137
.getMissionsByAstronaut(
141-
MissionServiceRequest(0, astronaut.id),
138+
MissionServiceRequest(0, astronautId),
142139
environment
143140
)
144141
}
145142

146143
private val planetService = PlanetService()
147144
private val planetsByMissionDataFetcher = DataFetcher { environment ->
148-
val mission = environment.getSource<Mission>()
145+
val missionId = environment.getSource<Mission>()?.id ?: throw IllegalArgumentException("Mission ID is null")
149146
planetService.getPlanets(
150-
PlanetServiceRequest(0, mission.id),
147+
PlanetServiceRequest(0, missionId),
151148
environment
152149
)
153150
}
154151
private val planetsByAstronautDataFetcher = DataFetcher { environment ->
155-
val astronaut = environment.getSource<Astronaut>()
152+
val astronautId = environment.getSource<Astronaut>()?.id ?: throw IllegalArgumentException("Astronaut ID is null")
156153
astronautService.getPlanets(
157-
AstronautServiceRequest(astronaut.id),
154+
AstronautServiceRequest(astronautId),
158155
environment
159156
)
160157
}
@@ -197,11 +194,22 @@ object AstronautGraphQL {
197194
)
198195
)
199196

200-
fun execute(
197+
fun executeOperations(
201198
graphQL: GraphQL,
202199
queries: List<String>,
203200
dataLoaderInstrumentationStrategy: DataLoaderInstrumentationStrategy
204-
): Pair<List<ExecutionResult>, KotlinDataLoaderRegistry> {
201+
): Pair<List<Result<ExecutionResult>>, KotlinDataLoaderRegistry> =
202+
execute(
203+
graphQL,
204+
queries.map { query -> ExecutionInput.newExecutionInput(query).build() },
205+
dataLoaderInstrumentationStrategy
206+
)
207+
208+
fun execute(
209+
graphQL: GraphQL,
210+
executionInputs: List<ExecutionInput>,
211+
dataLoaderInstrumentationStrategy: DataLoaderInstrumentationStrategy
212+
): Pair<List<Result<ExecutionResult>>, KotlinDataLoaderRegistry> {
205213
val kotlinDataLoaderRegistry = spyk(
206214
KotlinDataLoaderRegistryFactory(
207215
AstronautDataLoader(),
@@ -214,30 +222,36 @@ object AstronautGraphQL {
214222
when (dataLoaderInstrumentationStrategy) {
215223
DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION ->
216224
SyncExecutionExhaustedState::class to SyncExecutionExhaustedState(
217-
queries.size,
225+
executionInputs.size,
218226
kotlinDataLoaderRegistry
219227
)
220228
DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED ->
221229
ExecutionLevelDispatchedState::class to ExecutionLevelDispatchedState(
222-
queries.size
230+
executionInputs.size
223231
)
224232
}
225233
)
226234

227235
val results = runBlocking {
228-
queries.map { query ->
236+
executionInputs.map { executionInput ->
229237
async {
230-
graphQL.executeAsync(
231-
ExecutionInput
232-
.newExecutionInput(query)
233-
.dataLoaderRegistry(kotlinDataLoaderRegistry)
234-
.graphQLContext(graphQLContext)
235-
.build()
236-
).await()
238+
try {
239+
Result.success(
240+
graphQL.executeAsync(
241+
executionInput.transform { builder ->
242+
builder
243+
.dataLoaderRegistry(kotlinDataLoaderRegistry)
244+
.graphQLContext(graphQLContext)
245+
.build()
246+
}
247+
).await()
248+
)
249+
} catch (e: Exception) {
250+
Result.failure(e)
251+
}
237252
}
238253
}.awaitAll()
239254
}
240-
241255
return Pair(results, kotlinDataLoaderRegistry)
242256
}
243257
}

executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/level/DataLoaderLevelDispatchedInstrumentationTest.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class DataLoaderLevelDispatchedInstrumentationTest {
4646
"{ mission(id: 4) { designation } }"
4747
)
4848

49-
val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute(
49+
val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations(
5050
graphQL,
5151
queries,
5252
DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED
@@ -77,7 +77,7 @@ class DataLoaderLevelDispatchedInstrumentationTest {
7777
"{ nasa { mission(id: 4) { id designation } } }"
7878
)
7979

80-
val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute(
80+
val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations(
8181
graphQL,
8282
queries,
8383
DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED
@@ -112,7 +112,7 @@ class DataLoaderLevelDispatchedInstrumentationTest {
112112
"{ mission(id: 4) { designation } }"
113113
)
114114

115-
val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute(
115+
val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations(
116116
graphQL,
117117
queries,
118118
DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED
@@ -147,7 +147,7 @@ class DataLoaderLevelDispatchedInstrumentationTest {
147147
"""mutation { createAstronaut(name: "spaceMan") { id name } }"""
148148
)
149149

150-
val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute(
150+
val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations(
151151
graphQL,
152152
queries,
153153
DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED
@@ -168,7 +168,7 @@ class DataLoaderLevelDispatchedInstrumentationTest {
168168
"{ mission(id: 4) { designation } }"
169169
)
170170

171-
val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute(
171+
val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations(
172172
graphQL,
173173
queries,
174174
DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED

0 commit comments

Comments
 (0)