Skip to content

ExecutorConfigurationSupport API refinement to control internal executor shutdown #24497

Closed
@ttddyy

Description

@ttddyy

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.

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions