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

ExecutorConfigurationSupport API refinement to control internal executor shutdown #24497

Closed
ttddyy opened this issue Feb 10, 2020 · 2 comments
Closed
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Milestone

Comments

@ttddyy
Copy link
Contributor

ttddyy commented Feb 10, 2020

Currently, ExecutorConfigurationSupport#shutdown() encapsulate multiple scenarios for shutting down internal ExecutorService.

The single method shutdown() performs shutdown()/shutdownNow()(non-blocking) and awaitTermination()(blocking) based on its property.

I am writing a graceful shutdown logic for task executor/scheduler.
The logic for graceful shutdown is to retrieve all task executor/schedulers and apply:

  • Call shutdown() to not accept anymore tasks
  • Wait currently running task for the duration of graceful period

With current available API, I need to do following:

Instant deadline = start.plus(gracefulShutdownTimeout);

// stop receiving anymore request while keep running active ones
for (ExecutorConfigurationSupport executorConfigurationSupport : this.executorConfigurationSupports) {
	executorConfigurationSupport.setWaitForTasksToCompleteOnShutdown(true);
	executorConfigurationSupport.shutdown(); // non-blocking
}

// Previously, executors are called "shutdown()"; so, no more new tasks are scheduled.
// Now, call "awaitTermination()" to wait current tasks to finish while
// the container is shutting down in parallel.
for (ExecutorConfigurationSupport executorConfigurationSupport : this.executorConfigurationSupports) {
	int awaitTerminationSeconds = Math.toIntExact(Duration.between(Instant.now(), deadline).getSeconds());
	executorConfigurationSupport.setAwaitTerminationSeconds(awaitTerminationSeconds);
	executorConfigurationSupport.shutdown();  // blocking
}

Since this calls shutdown() twice with different parameter in order to achieve shutdown() and awaitTermination() for underlying executor, it is not so intuitive. Also requires to know the detail about what ExecutorConfigurationSupport#shutdown() does.

Another workaround is to retrieve internal ExecutorService and call shutdown() and awaitTermination().

List<ExecutorService> executorServices = new ArrayList<>();

for (ExecutorConfigurationSupport executorConfigurationSupport : this.executorConfigurationSupports) {
	if (executorConfigurationSupport instanceof ThreadPoolTaskExecutor) {
		executorServices.add(((ThreadPoolTaskExecutor)executorConfigurationSupport).getThreadPoolExecutor());
	}
	else if (executorConfigurationSupport instanceof ThreadPoolTaskScheduler) {
		executorServices.add(((ThreadPoolTaskScheduler)executorConfigurationSupport).getScheduledExecutor());
	}
}

for(ExecutorService executorService : executorServices) {
	executorService.shutdown();
}

for(ExecutorService executorService : executorServices) {
	executorService.awaitTermination(...);
}

I think it would be nice to have some API refinement for task executor/scheduler to easily control underlying ExecutorService.

Simple solution is to to add a getter to ExecutorConfigurationSupport to expose internal ExecutorService. This way, in addition to existing shutdown(), if user needs to do more fine control on shutdown, getter can expose the ExecutorService.
Another way is to provide blocking(awaitTermination) and non-blocking(shutdown/shutdownNow) methods on ExecutorConfigurationSupport instead or in addition to the current shutdown() method.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Feb 10, 2020
@jhoeller jhoeller self-assigned this Feb 11, 2020
@jhoeller jhoeller added type: enhancement A general enhancement in: core Issues in core modules (aop, beans, core, context, expression) and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Feb 11, 2020
@jhoeller jhoeller added this to the 5.2.4 milestone Feb 11, 2020
@jhoeller jhoeller modified the milestones: 5.2.4, 5.2.5 Feb 20, 2020
@jhoeller jhoeller modified the milestones: 5.2.5, 5.3 M2 Mar 21, 2020
@jhoeller jhoeller modified the milestones: 5.3 M2, 5.x Backlog Jul 22, 2020
@jgslima
Copy link

jgslima commented Apr 6, 2022

I can see this issue is related to other issue I created: #27090

@jhoeller jhoeller modified the milestones: 6.x Backlog, 6.1.0-M2 Jul 5, 2023
@jhoeller jhoeller changed the title ExecutorConfigurationSupport API refinement to control internal executor shutdown ExecutorConfigurationSupport API refinement to control internal executor shutdown Jul 7, 2023
@jhoeller
Copy link
Contributor

jhoeller commented Jul 7, 2023

Along with #27090, we've introduced an initiateShutdown() method with non-blocking semantics which is what we trigger on ContextClosedEvent now but which can also be publicly called in scenarios like above, as an early signal before a full shutdown() step. That said, in standard application context scenarios, the implicit triggering from ContextClosedEvent should lead to a graceful shutdown already, delivering a round of early shutdown signals to the executors/schedulers before the lifecycle stop phase and ultimately a round of full shutdown() calls on the same executors/schedulers for actual destruction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants