From 8bf1df6baa1db8bd67c7db7c803351e647e0d8f6 Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Tue, 21 Nov 2023 10:30:32 -0800 Subject: [PATCH 1/3] Update example app to use chained data loader. --- .../dataLoader/GreetingsDataLoader.java | 42 +++++++++++++++++++ .../shared/datafetcher/HelloDataFetcher.java | 12 ++++++ .../src/main/resources/schema/schema.graphqls | 1 + 3 files changed, 55 insertions(+) create mode 100644 graphql-dgs-example-shared/src/main/java/com/netflix/graphql/dgs/example/shared/dataLoader/GreetingsDataLoader.java diff --git a/graphql-dgs-example-shared/src/main/java/com/netflix/graphql/dgs/example/shared/dataLoader/GreetingsDataLoader.java b/graphql-dgs-example-shared/src/main/java/com/netflix/graphql/dgs/example/shared/dataLoader/GreetingsDataLoader.java new file mode 100644 index 000000000..d8393700a --- /dev/null +++ b/graphql-dgs-example-shared/src/main/java/com/netflix/graphql/dgs/example/shared/dataLoader/GreetingsDataLoader.java @@ -0,0 +1,42 @@ +/* + * Copyright 2021 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.graphql.dgs.example.shared.dataLoader; + +import com.netflix.graphql.dgs.DgsDataLoader; +import com.netflix.graphql.dgs.DgsDispatchPredicate; +import org.dataloader.BatchLoader; +import org.dataloader.registries.DispatchPredicate; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; + +@DgsDataLoader(name = "greetings") +public class GreetingsDataLoader implements BatchLoader { + @Override + public CompletionStage> load(List keys) { + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return CompletableFuture.supplyAsync(() -> keys.stream().map(key -> "Greetings, " + key + "!").collect(Collectors.toList())); + } +} + diff --git a/graphql-dgs-example-shared/src/main/java/com/netflix/graphql/dgs/example/shared/datafetcher/HelloDataFetcher.java b/graphql-dgs-example-shared/src/main/java/com/netflix/graphql/dgs/example/shared/datafetcher/HelloDataFetcher.java index aafcafa08..1945291e9 100644 --- a/graphql-dgs-example-shared/src/main/java/com/netflix/graphql/dgs/example/shared/datafetcher/HelloDataFetcher.java +++ b/graphql-dgs-example-shared/src/main/java/com/netflix/graphql/dgs/example/shared/datafetcher/HelloDataFetcher.java @@ -50,9 +50,21 @@ public String hello(@InputArgument String name) { @DgsData(parentType = "Query", field = "messageFromBatchLoader") public CompletableFuture getMessage(DataFetchingEnvironment env) { DataLoader dataLoader = env.getDataLoader("messages"); + DataLoader dataLoaderB = env.getDataLoader("greetings"); return dataLoader.load("a"); } + @DgsData(parentType = "Query", field = "messageFromBatchLoaderWithGreetings") + public CompletableFuture getGreetings(DataFetchingEnvironment env) { + DataLoader dataLoaderA = env.getDataLoader("messages"); + DataLoader dataLoaderB = env.getDataLoader("greetings"); + return dataLoaderB.load("a").thenCompose(key -> { + CompletableFuture loadA = dataLoaderA.load(key); + dataLoaderA.dispatch(); + return loadA; + }); + } + @DgsData(parentType = "Query", field = "messageFromBatchLoaderWithScheduledDispatch") public CompletableFuture getMessageScheduled(DataFetchingEnvironment env) { DataLoader dataLoader = env.getDataLoader("messagesWithScheduledDispatch"); diff --git a/graphql-dgs-example-shared/src/main/resources/schema/schema.graphqls b/graphql-dgs-example-shared/src/main/resources/schema/schema.graphqls index 4edacc53e..9adb6e1bd 100644 --- a/graphql-dgs-example-shared/src/main/resources/schema/schema.graphqls +++ b/graphql-dgs-example-shared/src/main/resources/schema/schema.graphqls @@ -6,6 +6,7 @@ type Query { withDataLoaderGraphQLContext: String movies: [Movie] messageFromBatchLoader: String + messageFromBatchLoaderWithGreetings: String messagesWithExceptionFromBatchLoader: [Message] messageFromBatchLoaderWithScheduledDispatch: String From 3b7bd193f14605fa29759ca1348285fc6e58697d Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Tue, 21 Nov 2023 10:49:19 -0800 Subject: [PATCH 2/3] Add a new config property for the schedule duration in ticker mode. --- .../netflix/graphql/dgs/autoconfig/DgsAutoConfiguration.kt | 2 +- .../graphql/dgs/autoconfig/DgsConfigurationProperties.kt | 5 ++++- .../netflix/graphql/dgs/internal/DgsDataLoaderProvider.kt | 6 +++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsAutoConfiguration.kt b/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsAutoConfiguration.kt index ebe8638c7..1a109f9dc 100644 --- a/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsAutoConfiguration.kt +++ b/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsAutoConfiguration.kt @@ -164,7 +164,7 @@ open class DgsAutoConfiguration( @Bean open fun dgsDataLoaderProvider(applicationContext: ApplicationContext, dataloaderOptionProvider: DgsDataLoaderOptionsProvider, @Qualifier("dgsScheduledExecutorService") dgsScheduledExecutorService: ScheduledExecutorService): DgsDataLoaderProvider { - return DgsDataLoaderProvider(applicationContext, dataloaderOptionProvider, dgsScheduledExecutorService, configProps.dataloaderTickerModeEnabled) + return DgsDataLoaderProvider(applicationContext, dataloaderOptionProvider, dgsScheduledExecutorService, configProps.dataloaderScheduleDuration, configProps.dataloaderTickerModeEnabled) } /** diff --git a/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsConfigurationProperties.kt b/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsConfigurationProperties.kt index 6e2715ce6..334fa4db7 100644 --- a/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsConfigurationProperties.kt +++ b/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsConfigurationProperties.kt @@ -19,6 +19,7 @@ package com.netflix.graphql.dgs.autoconfig import com.netflix.graphql.dgs.internal.DgsSchemaProvider.Companion.DEFAULT_SCHEMA_LOCATION import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.context.properties.bind.DefaultValue +import java.time.Duration /** * Configuration properties for DGS framework. @@ -31,9 +32,11 @@ data class DgsConfigurationProperties( @DefaultValue("true") val schemaWiringValidationEnabled: Boolean, @DefaultValue("false") val enableEntityFetcherCustomScalarParsing: Boolean, /** Data loader properties.*/ - @DefaultValue("false") val dataloaderTickerModeEnabled: Boolean + @DefaultValue("false") val dataloaderTickerModeEnabled: Boolean, + @DefaultValue(DATALOADER_DEFAULT_SCHEDULE_DURATION) val dataloaderScheduleDuration: Duration ) { companion object { const val PREFIX: String = "dgs.graphql" + const val DATALOADER_DEFAULT_SCHEDULE_DURATION = "10ms" } } diff --git a/graphql-dgs/src/main/kotlin/com/netflix/graphql/dgs/internal/DgsDataLoaderProvider.kt b/graphql-dgs/src/main/kotlin/com/netflix/graphql/dgs/internal/DgsDataLoaderProvider.kt index 2cc2396bd..6ae0a68ff 100644 --- a/graphql-dgs/src/main/kotlin/com/netflix/graphql/dgs/internal/DgsDataLoaderProvider.kt +++ b/graphql-dgs/src/main/kotlin/com/netflix/graphql/dgs/internal/DgsDataLoaderProvider.kt @@ -38,6 +38,7 @@ import org.springframework.aop.support.AopUtils import org.springframework.beans.factory.NoSuchBeanDefinitionException import org.springframework.context.ApplicationContext import org.springframework.util.ReflectionUtils +import java.time.Duration import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import java.util.function.Supplier @@ -50,6 +51,7 @@ class DgsDataLoaderProvider( private val applicationContext: ApplicationContext, private val dataLoaderOptionsProvider: DgsDataLoaderOptionsProvider = DefaultDataLoaderOptionsProvider(), private val scheduledExecutorService: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor(), + private val scheduleDuration: Duration = Duration.ofMillis(10), private val enableTickerMode: Boolean = false ) { @@ -69,9 +71,7 @@ class DgsDataLoaderProvider( fun buildRegistryWithContextSupplier(contextSupplier: Supplier): DataLoaderRegistry { // We need to set the default predicate to 20ms and individually override with DISPATCH_ALWAYS or the custom dispatch predicate, if specified // The data loader ends up applying the overall dispatch predicate when the custom dispatch predicate is not true otherwise. - val registry = ScheduledDataLoaderRegistry.newScheduledRegistry().scheduledExecutorService(scheduledExecutorService).tickerMode(enableTickerMode).dispatchPredicate( - DispatchPredicate.DISPATCH_NEVER - ).build() + val registry = ScheduledDataLoaderRegistry.newScheduledRegistry().scheduledExecutorService(scheduledExecutorService).tickerMode(enableTickerMode).schedule(scheduleDuration).dispatchPredicate(DispatchPredicate.DISPATCH_NEVER).build() val totalTime = measureTimeMillis { val extensionProviders = applicationContext From b0538ab2b27c2f9e9c5ca07de7fc6a710f882ac9 Mon Sep 17 00:00:00 2001 From: ksrinivasan Date: Tue, 21 Nov 2023 11:19:45 -0800 Subject: [PATCH 3/3] Separate the config for data loaders. --- .../dgs/autoconfig/DgsAutoConfiguration.kt | 7 ++-- .../autoconfig/DgsConfigurationProperties.kt | 8 ++--- .../DgsDataloaderConfigurationProperties.kt | 36 +++++++++++++++++++ 3 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsDataloaderConfigurationProperties.kt diff --git a/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsAutoConfiguration.kt b/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsAutoConfiguration.kt index 1a109f9dc..05153f570 100644 --- a/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsAutoConfiguration.kt +++ b/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsAutoConfiguration.kt @@ -78,10 +78,11 @@ import java.util.concurrent.ScheduledExecutorService */ @Suppress("SpringJavaInjectionPointsAutowiringInspection") @AutoConfiguration -@EnableConfigurationProperties(DgsConfigurationProperties::class) +@EnableConfigurationProperties(DgsConfigurationProperties::class, DgsDataloaderConfigurationProperties::class) @ImportAutoConfiguration(classes = [JacksonAutoConfiguration::class, DgsInputArgumentConfiguration::class]) open class DgsAutoConfiguration( - private val configProps: DgsConfigurationProperties + private val configProps: DgsConfigurationProperties, + private val dataloaderConfigProps: DgsDataloaderConfigurationProperties ) { companion object { @@ -164,7 +165,7 @@ open class DgsAutoConfiguration( @Bean open fun dgsDataLoaderProvider(applicationContext: ApplicationContext, dataloaderOptionProvider: DgsDataLoaderOptionsProvider, @Qualifier("dgsScheduledExecutorService") dgsScheduledExecutorService: ScheduledExecutorService): DgsDataLoaderProvider { - return DgsDataLoaderProvider(applicationContext, dataloaderOptionProvider, dgsScheduledExecutorService, configProps.dataloaderScheduleDuration, configProps.dataloaderTickerModeEnabled) + return DgsDataLoaderProvider(applicationContext, dataloaderOptionProvider, dgsScheduledExecutorService, dataloaderConfigProps.scheduleDuration, dataloaderConfigProps.tickerModeEnabled) } /** diff --git a/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsConfigurationProperties.kt b/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsConfigurationProperties.kt index 334fa4db7..ea8e9149c 100644 --- a/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsConfigurationProperties.kt +++ b/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsConfigurationProperties.kt @@ -19,7 +19,6 @@ package com.netflix.graphql.dgs.autoconfig import com.netflix.graphql.dgs.internal.DgsSchemaProvider.Companion.DEFAULT_SCHEMA_LOCATION import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.context.properties.bind.DefaultValue -import java.time.Duration /** * Configuration properties for DGS framework. @@ -30,13 +29,10 @@ data class DgsConfigurationProperties( /** Location of the GraphQL schema files. */ @DefaultValue(DEFAULT_SCHEMA_LOCATION) val schemaLocations: List, @DefaultValue("true") val schemaWiringValidationEnabled: Boolean, - @DefaultValue("false") val enableEntityFetcherCustomScalarParsing: Boolean, - /** Data loader properties.*/ - @DefaultValue("false") val dataloaderTickerModeEnabled: Boolean, - @DefaultValue(DATALOADER_DEFAULT_SCHEDULE_DURATION) val dataloaderScheduleDuration: Duration + @DefaultValue("false") val enableEntityFetcherCustomScalarParsing: Boolean ) { + companion object { const val PREFIX: String = "dgs.graphql" - const val DATALOADER_DEFAULT_SCHEDULE_DURATION = "10ms" } } diff --git a/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsDataloaderConfigurationProperties.kt b/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsDataloaderConfigurationProperties.kt new file mode 100644 index 000000000..74def7b94 --- /dev/null +++ b/graphql-dgs-spring-boot-oss-autoconfigure/src/main/kotlin/com/netflix/graphql/dgs/autoconfig/DgsDataloaderConfigurationProperties.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2021 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.graphql.dgs.autoconfig + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.context.properties.bind.DefaultValue +import java.time.Duration + +/** + * Configuration properties for DGS framework. + */ +@ConfigurationProperties(prefix = DgsDataloaderConfigurationProperties.DATALOADER_PREFIX) +@Suppress("ConfigurationProperties") +data class DgsDataloaderConfigurationProperties( + @DefaultValue("false") val tickerModeEnabled: Boolean, + @DefaultValue(DATALOADER_DEFAULT_SCHEDULE_DURATION) val scheduleDuration: Duration +) { + companion object { + const val DATALOADER_PREFIX: String = "dgs.graphql.dataloader" + const val DATALOADER_DEFAULT_SCHEDULE_DURATION = "10ms" + } +}