Skip to content
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

Update set up for ScheduledExecutorService. #1705

Merged
merged 1 commit into from
Nov 16, 2023

Conversation

srinivasankavitha
Copy link
Contributor

@srinivasankavitha srinivasankavitha commented Nov 9, 2023

This PR has the following changes:

  1. Inject a ScheduledExecutorService bean for use across requests, qualified with @DgsScheduledExecutorService
  2. Update to use the latest java-dataloader 3.2.2 to pick up [this fix] to avoid instantiating a new service per request (Lazily initialize Executor in ScheduledDataLoaderRegistry builder graphql-java/java-dataloader#135)
  3. Make the tickerMode configurable, default set to false

return DgsDataLoaderProvider(applicationContext, dataloaderOptionProvider)
@ConditionalOnMissingBean
open fun scheduledExecutorService(): ScheduledExecutorService {
return Executors.newSingleThreadScheduledExecutor()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will cause problems if someone is trying to autowire an Executor bean, since ScheduledExecutorService also implements Executor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, yeah I am changing it to add a quaifier for dgsScheduledExecutorService. Would that mitigate the issue you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, a Qualifier would avoid the problem

@@ -154,8 +156,14 @@ open class DgsAutoConfiguration(
}

@Bean
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we set destroyMethod="shutdown"?

Copy link
Collaborator

@paulbakker paulbakker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the pool size for the scheduled executor should be configurable, but otherwise looks great.

@@ -45,7 +48,9 @@ import kotlin.system.measureTimeMillis
*/
class DgsDataLoaderProvider(
private val applicationContext: ApplicationContext,
private val dataLoaderOptionsProvider: DgsDataLoaderOptionsProvider = DefaultDataLoaderOptionsProvider()
private val dataLoaderOptionsProvider: DgsDataLoaderOptionsProvider = DefaultDataLoaderOptionsProvider(),
private val scheduledExecutorService: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really know when/why to use singleThreadScheduleExecutor vs a pool of scheduled threads with Executors.newScheduledThreadPool(int corePoolSize). It might be good to have this configurable, but I'm not sure.

Copy link
Contributor Author

@srinivasankavitha srinivasankavitha Nov 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is set up as a bean in our autoconfig, I'm thinking the the user can override the dgsScheduledExecutorService bean to set it up differently?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also thought about that and am not sure what a good default is. It probably depends on the load / RPS. If you have a lot of events scheduled at or around the same instant I can see one thread becoming a bottleneck; but I also think it's something that could be solved in a follow up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the input folks! The default is just based on what the java-dataloader library already picked. Will merge this PR for now and we can make it configurable if needed in a follow up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but the default uses a new executor for every registry created. That's in and of itself not ideal and runs into its own scaling problem of spinning up too many threads, but it wouldn't run into this specific scaling problem where an executor is shared since it's essentially spinning up a thread per request. I suspect a default like corePoolSize set to the number of CPUs might make more sense. But if the default here is a problem we'll see it in testing, and can fix it in a follow up like you said.

@@ -118,7 +119,12 @@ object BaseDgsQueryExecutor {
.extensions(extensions.orEmpty())
.build()
graphQLContextFuture.complete(executionInput.graphQLContext)
graphQL.executeAsync(executionInput)
graphQL.executeAsync(executionInput).thenApply {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whenComplete might be safer, although I don't know under what circumstances executeAsync ever really fails; maybe cancellation?

Add dgs qualifier for scheduled executor service.
Update to java-dataloader 3.2.2 containing a fix for memory leak.
Close the scheduled dataloader registry upon request completion.
Use whenComplete instead of Apply for closing registry.
Fix linter errors.
@srinivasankavitha srinivasankavitha merged commit 7871fb9 into master Nov 16, 2023
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants