Search code examples
spring-integrationgraceful-shutdown

Spring Integration Graceful Shutdown with TaskExecutor


I have a Spring Boot web application which shuts down gracefully after a timeout of 120secs.
Recently we have added a Spring Integration Flow to the same application to be able to process some files from a directory.
The poller uses a TaskExecutor to process files in multiple threads (5 for now).

public TaskExecutor chunkExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(5);
    taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    taskExecutor.setAwaitTerminationSeconds(120);
    taskExecutor.setThreadNamePrefix("customExecutor-");
    return taskExecutor;
}

PollerSpec looks like:

public void accept(SourcePollingChannelAdapterSpec sourcePollingChannelAdapterSpec) {
    sourcePollingChannelAdapterSpec.poller(Pollers.fixedDelay(fixedDelay)
            .taskExecutor(chunkExecutor)
            .maxMessagesPerPoll(maxMessagesPerPoll)
            .transactionSynchronizationFactory(transactionSyncFactory)
            .transactional(new PseudoTransactionManager()));
}

enter image description here

We have noticed that the Spring Integration flow does not follow the graceful shutdown of the main application. In some instances, the poller even picks up files after the tomcat is shutdown.

If we don't use the taskExecutor, flow looks good

Is there anything else I need to do to make this Spring Integration flow shutdown gracefully like the main Spring Boot application while using the taskExecutor?


Update:
Based on Artem's answer on TaskScheduling, I commented out the taskExecutor in the poller spec and added the scheduling properties.
However I noticed that there is only one thread running at any point in time even though the pool size is 2. Am I missing something?
Updated spec:
spring:
  task:
    scheduling:
      thread-name-prefix: "customScheduling-"
      pool:
        size: 2
      shutdown:
        await-termination-period: "120s"
        await-termination: true

PollerSpec:

    public void accept(SourcePollingChannelAdapterSpec sourcePollingChannelAdapterSpec) {
    sourcePollingChannelAdapterSpec.poller(Pollers.fixedDelay(fixedDelay)
            //.taskExecutor(chunkExecutor)
            .maxMessagesPerPoll(maxMessagesPerPoll)
            .transactionSynchronizationFactory(transactionSyncFactory)
            .transactional(new PseudoTransactionManager()));
}

From the logs I noticed that the tomcat has already shutdown while the scheduler is waiting for a REST call to complete.


Solution

  • Like Artem mentioned, the shutdown was really for the tomcat and not for the async tasks. I was able to have a graceful shutdown of the executor by implementing an EventListener for onContextClosedEvent and call the shutDown on the taskExecutor. This invokes the graceful shutdown of the taskExecutor

    @EventListener
    public void onContextClosedEvent(ContextClosedEvent event) {
        taskExecutor.shutdown();
    }