From b3795bb07f9e86f0b1beb4118a1f5bb0fec08634 Mon Sep 17 00:00:00 2001 From: Samuel Vazquez Date: Thu, 11 Jul 2024 14:15:41 -0700 Subject: [PATCH] feat(batching): v7 check if execution was exhausted when there are errors (#2010) ### :pencil: Description https://github.com/ExpediaGroup/graphql-kotlin/pull/2009 --------- Co-authored-by: Samuel Vazquez --- ...erSyncExecutionExhaustedInstrumentation.kt | 2 +- ...ctSyncExecutionExhaustedInstrumentation.kt | 9 ++- ...utionExhaustedInstrumentationParameters.kt | 6 +- .../state/SyncExecutionExhaustedState.kt | 26 +++---- .../fixture/AstronautGraphQL.kt | 72 +++++++++++-------- ...oaderLevelDispatchedInstrumentationTest.kt | 10 +-- ...ncExecutionExhaustedInstrumentationTest.kt | 66 ++++++++++++----- 7 files changed, 118 insertions(+), 73 deletions(-) diff --git a/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/DataLoaderSyncExecutionExhaustedInstrumentation.kt b/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/DataLoaderSyncExecutionExhaustedInstrumentation.kt index 209b61bcbb..358c7abed7 100644 --- a/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/DataLoaderSyncExecutionExhaustedInstrumentation.kt +++ b/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/DataLoaderSyncExecutionExhaustedInstrumentation.kt @@ -40,7 +40,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentation : AbstractSyncExecutionExh parameters: SyncExecutionExhaustedInstrumentationParameters ): OnSyncExecutionExhaustedCallback = { _: List -> parameters - .executionContext.executionInput + .executionInput .dataLoaderRegistry .dispatchAll() } diff --git a/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/execution/AbstractSyncExecutionExhaustedInstrumentation.kt b/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/execution/AbstractSyncExecutionExhaustedInstrumentation.kt index 8d06df8f85..b6e07f401d 100644 --- a/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/execution/AbstractSyncExecutionExhaustedInstrumentation.kt +++ b/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/execution/AbstractSyncExecutionExhaustedInstrumentation.kt @@ -59,7 +59,12 @@ abstract class AbstractSyncExecutionExhaustedInstrumentation : SimplePerformantI ): InstrumentationContext? = parameters.graphQLContext ?.get(SyncExecutionExhaustedState::class) - ?.beginExecution(parameters) + ?.beginExecution( + parameters, + this.getOnSyncExecutionExhaustedCallback( + SyncExecutionExhaustedInstrumentationParameters(parameters.executionInput) + ) + ) override fun beginExecutionStrategy( parameters: InstrumentationExecutionStrategyParameters, @@ -78,7 +83,7 @@ abstract class AbstractSyncExecutionExhaustedInstrumentation : SimplePerformantI ?.beginFieldFetch( parameters, this.getOnSyncExecutionExhaustedCallback( - SyncExecutionExhaustedInstrumentationParameters(parameters.executionContext) + SyncExecutionExhaustedInstrumentationParameters(parameters.executionContext.executionInput) ) ) } diff --git a/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/execution/SyncExecutionExhaustedInstrumentationParameters.kt b/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/execution/SyncExecutionExhaustedInstrumentationParameters.kt index 1e955de7ab..38d8f8c58d 100644 --- a/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/execution/SyncExecutionExhaustedInstrumentationParameters.kt +++ b/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/execution/SyncExecutionExhaustedInstrumentationParameters.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 Expedia, Inc + * Copyright 2024 Expedia, Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,11 +17,11 @@ package com.expediagroup.graphql.dataloader.instrumentation.syncexhaustion.execution import com.expediagroup.graphql.dataloader.instrumentation.syncexhaustion.DataLoaderSyncExecutionExhaustedInstrumentation -import graphql.execution.ExecutionContext +import graphql.ExecutionInput /** * Hold information that will be provided to an instance of [DataLoaderSyncExecutionExhaustedInstrumentation] */ data class SyncExecutionExhaustedInstrumentationParameters( - val executionContext: ExecutionContext + val executionInput: ExecutionInput ) diff --git a/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/state/SyncExecutionExhaustedState.kt b/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/state/SyncExecutionExhaustedState.kt index c8cb8eb599..95501fa33b 100644 --- a/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/state/SyncExecutionExhaustedState.kt +++ b/executions/graphql-kotlin-dataloader-instrumentation/src/main/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/state/SyncExecutionExhaustedState.kt @@ -46,20 +46,6 @@ class SyncExecutionExhaustedState( private val totalExecutions: AtomicReference = AtomicReference(totalOperations) val executions = ConcurrentHashMap() - /** - * Remove an [ExecutionBatchState] from the state in case operation does not qualify for starting an execution, - * for example: - * - parsing, validation errors - * - persisted query errors - * - an exception during execution was thrown - */ - private fun removeExecution(executionId: ExecutionId) { - if (executions.containsKey(executionId)) { - executions.remove(executionId) - totalExecutions.set(totalExecutions.get() - 1) - } - } - /** * Create the [ExecutionBatchState] When a specific [ExecutionInput] starts his execution * @@ -67,7 +53,8 @@ class SyncExecutionExhaustedState( * @return a non null [InstrumentationContext] object */ fun beginExecution( - parameters: InstrumentationExecutionParameters + parameters: InstrumentationExecutionParameters, + onSyncExecutionExhausted: OnSyncExecutionExhaustedCallback ): InstrumentationContext { executions.computeIfAbsent(parameters.executionInput.executionId) { ExecutionBatchState() @@ -75,7 +62,14 @@ class SyncExecutionExhaustedState( return object : SimpleInstrumentationContext() { override fun onCompleted(result: ExecutionResult?, t: Throwable?) { if ((result != null && result.errors.size > 0) || t != null) { - removeExecution(parameters.executionInput.executionId) + if (executions.containsKey(parameters.executionInput.executionId)) { + executions.remove(parameters.executionInput.executionId) + totalExecutions.set(totalExecutions.get() - 1) + val allSyncExecutionsExhausted = allSyncExecutionsExhausted() + if (allSyncExecutionsExhausted) { + onSyncExecutionExhausted(executions.keys().toList()) + } + } } } } diff --git a/executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/fixture/AstronautGraphQL.kt b/executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/fixture/AstronautGraphQL.kt index 4757c0dbf0..ec4b58a059 100644 --- a/executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/fixture/AstronautGraphQL.kt +++ b/executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/fixture/AstronautGraphQL.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 Expedia, Inc + * Copyright 2024 Expedia, Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -94,18 +94,16 @@ object AstronautGraphQL { private val astronautService = AstronautService() private val astronautDataFetcher = DataFetcher { environment -> + val astronautId = environment.getArgument("id")?.toInt() ?: throw IllegalArgumentException("Astronaut ID is null") astronautService.getAstronaut( - AstronautServiceRequest( - environment.getArgument("id").toInt() - ), + AstronautServiceRequest(astronautId), environment ) } private val createAstronautDataFetcher = DataFetcher { environment -> + val astronautName = environment.getArgument("name") ?: throw IllegalArgumentException("Astronaut name is null") astronautService.createAstronaut( - CreateAstronautServiceRequest( - environment.getArgument("name") - ) + CreateAstronautServiceRequest(astronautName) ) } private val astronautsDataFetcher = DataFetcher { environment -> @@ -119,10 +117,9 @@ object AstronautGraphQL { private val missionService = MissionService() private val missionDataFetcher = DataFetcher { environment -> + val missionId = environment.getArgument("id")?.toInt() ?: throw IllegalArgumentException("Mission ID is null") missionService.getMission( - MissionServiceRequest( - environment.getArgument("id").toInt() - ), + MissionServiceRequest(missionId), environment ) } @@ -135,26 +132,26 @@ object AstronautGraphQL { ) } private val missionsByAstronautDataFetcher = DataFetcher { environment -> - val astronaut = environment.getSource() + val astronautId = environment.getSource()?.id ?: throw IllegalArgumentException("Astronaut ID is null") missionService .getMissionsByAstronaut( - MissionServiceRequest(0, astronaut.id), + MissionServiceRequest(0, astronautId), environment ) } private val planetService = PlanetService() private val planetsByMissionDataFetcher = DataFetcher { environment -> - val mission = environment.getSource() + val missionId = environment.getSource()?.id ?: throw IllegalArgumentException("Mission ID is null") planetService.getPlanets( - PlanetServiceRequest(0, mission.id), + PlanetServiceRequest(0, missionId), environment ) } private val planetsByAstronautDataFetcher = DataFetcher { environment -> - val astronaut = environment.getSource() + val astronautId = environment.getSource()?.id ?: throw IllegalArgumentException("Astronaut ID is null") astronautService.getPlanets( - AstronautServiceRequest(astronaut.id), + AstronautServiceRequest(astronautId), environment ) } @@ -197,11 +194,22 @@ object AstronautGraphQL { ) ) - fun execute( + fun executeOperations( graphQL: GraphQL, queries: List, dataLoaderInstrumentationStrategy: DataLoaderInstrumentationStrategy - ): Pair, KotlinDataLoaderRegistry> { + ): Pair>, KotlinDataLoaderRegistry> = + execute( + graphQL, + queries.map { query -> ExecutionInput.newExecutionInput(query).build() }, + dataLoaderInstrumentationStrategy + ) + + fun execute( + graphQL: GraphQL, + executionInputs: List, + dataLoaderInstrumentationStrategy: DataLoaderInstrumentationStrategy + ): Pair>, KotlinDataLoaderRegistry> { val kotlinDataLoaderRegistry = spyk( KotlinDataLoaderRegistryFactory( AstronautDataLoader(), @@ -214,30 +222,36 @@ object AstronautGraphQL { when (dataLoaderInstrumentationStrategy) { DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION -> SyncExecutionExhaustedState::class to SyncExecutionExhaustedState( - queries.size, + executionInputs.size, kotlinDataLoaderRegistry ) DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED -> ExecutionLevelDispatchedState::class to ExecutionLevelDispatchedState( - queries.size + executionInputs.size ) } ) val results = runBlocking { - queries.map { query -> + executionInputs.map { executionInput -> async { - graphQL.executeAsync( - ExecutionInput - .newExecutionInput(query) - .dataLoaderRegistry(kotlinDataLoaderRegistry) - .graphQLContext(graphQLContext) - .build() - ).await() + try { + Result.success( + graphQL.executeAsync( + executionInput.transform { builder -> + builder + .dataLoaderRegistry(kotlinDataLoaderRegistry) + .graphQLContext(graphQLContext) + .build() + } + ).await() + ) + } catch (e: Exception) { + Result.failure(e) + } } }.awaitAll() } - return Pair(results, kotlinDataLoaderRegistry) } } diff --git a/executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/level/DataLoaderLevelDispatchedInstrumentationTest.kt b/executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/level/DataLoaderLevelDispatchedInstrumentationTest.kt index 9fde9a5082..b63ff37747 100644 --- a/executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/level/DataLoaderLevelDispatchedInstrumentationTest.kt +++ b/executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/level/DataLoaderLevelDispatchedInstrumentationTest.kt @@ -46,7 +46,7 @@ class DataLoaderLevelDispatchedInstrumentationTest { "{ mission(id: 4) { designation } }" ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED @@ -77,7 +77,7 @@ class DataLoaderLevelDispatchedInstrumentationTest { "{ nasa { mission(id: 4) { id designation } } }" ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED @@ -112,7 +112,7 @@ class DataLoaderLevelDispatchedInstrumentationTest { "{ mission(id: 4) { designation } }" ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED @@ -147,7 +147,7 @@ class DataLoaderLevelDispatchedInstrumentationTest { """mutation { createAstronaut(name: "spaceMan") { id name } }""" ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED @@ -168,7 +168,7 @@ class DataLoaderLevelDispatchedInstrumentationTest { "{ mission(id: 4) { designation } }" ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED diff --git a/executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/DataLoaderSyncExecutionExhaustedInstrumentationTest.kt b/executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/DataLoaderSyncExecutionExhaustedInstrumentationTest.kt index 4147d49570..b2f972f6f3 100644 --- a/executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/DataLoaderSyncExecutionExhaustedInstrumentationTest.kt +++ b/executions/graphql-kotlin-dataloader-instrumentation/src/test/kotlin/com/expediagroup/graphql/dataloader/instrumentation/syncexhaustion/DataLoaderSyncExecutionExhaustedInstrumentationTest.kt @@ -19,6 +19,7 @@ package com.expediagroup.graphql.dataloader.instrumentation.syncexhaustion import com.expediagroup.graphql.dataloader.instrumentation.fixture.DataLoaderInstrumentationStrategy import com.expediagroup.graphql.dataloader.instrumentation.fixture.AstronautGraphQL import com.expediagroup.graphql.dataloader.instrumentation.fixture.ProductGraphQL +import graphql.ExecutionInput import io.mockk.clearAllMocks import io.mockk.verify import org.junit.jupiter.api.BeforeEach @@ -54,7 +55,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { "{ mission(id: 4) { designation } }" ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION @@ -85,7 +86,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { "{ nasa { mission(id: 4) { id designation } } }" ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION @@ -120,7 +121,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { "{ mission(id: 4) { designation } }" ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION @@ -164,7 +165,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { """.trimIndent() ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION @@ -202,7 +203,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { """.trimIndent() ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION @@ -253,7 +254,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { """.trimIndent() ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION @@ -299,7 +300,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { """.trimIndent() ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION @@ -340,7 +341,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { """.trimIndent() ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION @@ -380,7 +381,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { """.trimIndent() ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION @@ -389,7 +390,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { assertEquals(3, results.size) results.forEach { result -> - assertTrue(result.errors.isEmpty()) + assertTrue(result.getOrThrow().errors.isEmpty()) } val missionStatistics = kotlinDataLoaderRegistry.dataLoadersMap["MissionDataLoader"]?.statistics @@ -422,7 +423,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { """.trimIndent() ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION @@ -430,7 +431,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { assertEquals(3, results.size) results.forEach { result -> - assertTrue(result.errors.isEmpty()) + assertTrue(result.getOrThrow().errors.isEmpty()) } val astronautStatistics = kotlinDataLoaderRegistry.dataLoadersMap["AstronautDataLoader"]?.statistics @@ -470,7 +471,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { """.trimIndent(), ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION @@ -482,7 +483,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { assertEquals(2, results.size) results.forEach { result -> - assertTrue(result.errors.isEmpty()) + assertTrue(result.getOrThrow().errors.isEmpty()) } assertEquals(1, astronautStatistics?.batchInvokeCount) @@ -566,7 +567,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { """mutation { createAstronaut(name: "spaceMan") { id name } }""" ) - val (results, dataLoaderSyncExecutionExhaustedInstrumentation) = AstronautGraphQL.execute( + val (results, dataLoaderSyncExecutionExhaustedInstrumentation) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.LEVEL_DISPATCHED @@ -579,7 +580,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { } @Test - fun `Instrumentation should not account for invalid operations`() { + fun `Instrumentation should not consider executions with invalid operations`() { val queries = listOf( "invalid query{ astronaut(id: 1) {", "{ astronaut(id: 2) { id name } }", @@ -587,7 +588,7 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { "{ mission(id: 4) { designation } }" ) - val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.executeOperations( graphQL, queries, DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION @@ -608,4 +609,35 @@ class DataLoaderSyncExecutionExhaustedInstrumentationTest { kotlinDataLoaderRegistry.dispatchAll() } } + + @Test + fun `Instrumentation should not consider executions that thrown exceptions`() { + val executions = listOf( + ExecutionInput.newExecutionInput("query test1 { astronaut(id: 1) { id name } }").operationName("test1").build(), + ExecutionInput.newExecutionInput("query test2 { astronaut(id: 2) { id name } }").operationName("test2").build(), + ExecutionInput.newExecutionInput("query test3 { mission(id: 3) { id designation } }").operationName("test3").build(), + ExecutionInput.newExecutionInput("query test4 { mission(id: 4) { designation } }").operationName("OPERATION_NOT_IN_DOCUMENT").build() + ) + + val (results, kotlinDataLoaderRegistry) = AstronautGraphQL.execute( + graphQL, + executions, + DataLoaderInstrumentationStrategy.SYNC_EXHAUSTION + ) + + assertEquals(4, results.size) + + val astronautStatistics = kotlinDataLoaderRegistry.dataLoadersMap["AstronautDataLoader"]?.statistics + val missionStatistics = kotlinDataLoaderRegistry.dataLoadersMap["MissionDataLoader"]?.statistics + + assertEquals(1, astronautStatistics?.batchInvokeCount) + assertEquals(2, astronautStatistics?.batchLoadCount) + + assertEquals(1, missionStatistics?.batchInvokeCount) + assertEquals(1, missionStatistics?.batchLoadCount) + + verify(exactly = 2) { + kotlinDataLoaderRegistry.dispatchAll() + } + } }