The following configuration shuts down instantly when the application is stopped.

If I'm not missing any configuration, this might be a bug?

@Configuration
public class ScheduledGracefulShutdownConfig {
        //this shouldn't be necessary at all with ' spring.task.scheduling.shutdown.await-termination'. but neither works
    @Bean
    TaskSchedulerCustomizer taskSchedulerCustomizer() {
        return taskScheduler -> {
            taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
            taskScheduler.setAwaitTerminationSeconds(60);
        };
    }
}

@SpringBootApplication
@EnableScheduling
@EnableAsync
public class AppConfiguration {
    public static void main(String[] args) {
        SpringApplication.run(AppConfiguration.class, args);
    }
}

@Service
public class ScheduledService {
    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    @Scheduled(fixedRate = 100000000L)
    public void scheduled() throws InterruptedException {
        LOGGER.info("Starting scheduled job...");
        TimeUnit.MINUTES.sleep(5);
        LOGGER.info("Scheduled job finished.");
    }
}

application.properties:

server.shutdown=graceful
server.shutdown.grace-period=30s
spring.task.execution.shutdown.await-termination=true
spring.task.execution.shutdown.await-termination-period=30s
spring.task.scheduling.shutdown.await-termination=true
spring.task.scheduling.shutdown.await-termination-period=30s

spring-boot-2.6.6 with spring-boot-starter-web dependency.

2022-04-06 12:36:46.237  INFO 256621 --- [   scheduling-1] ScheduledService     : Starting scheduled job...
2022-04-06 12:36:49.496  INFO 256621 --- [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown        : Commencing graceful shutdown. Waiting for active requests to complete
2022-04-06 12:36:49.499  INFO 256621 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown        : Graceful shutdown complete
2022-04-06 12:36:49.506 ERROR 256621 --- [   scheduling-1] o.s.s.s.TaskUtils$LoggingErrorHandler    : Unexpected error occurred in scheduled task

java.lang.InterruptedException: sleep interrupted
    at java.base/java.lang.Thread.sleep(Native Method) ~[na:na]
    at java.base/java.lang.Thread.sleep(Thread.java:334) ~[na:na]
    at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446) ~[na:na]
    at ScheduledService.scheduled(ScheduledService.java:17) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) ~[spring-context-5.3.17.jar:5.3.17]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-5.3.17.jar:5.3.17]
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
    at java.base/java.util.concurrent.FutureTask.runAndReset$$$capture(FutureTask.java:305) ~[na:na]
    at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java) ~[na:na]
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
    at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]
Process finished with exit code 130 (interrupted by signal 2: SIGINT)

Comment From: wilkinsona

This is the standard behaviour of Framework's scheduling support. When destroy is called on ScheduledAnnotationBeanPostProcessor, it cancels all of its scheduled tasks and interrupts them if they're running. There's no guarantee that interruption will do anything as it depends on the task implementation. When it does not do anything and the task continues to run, awaiting termination then comes into play.

Comment From: membersound

But what's the sense of spring.task.scheduling.shutdown.await-termination if any scheduled task is directly destroyed?

So how could I then delay the shutdown if a @Scheduled method has not yet finished, for example a batch-updating database flow in a long-running for-each loop inside the scheduled method?

Comment From: wilkinsona

if any scheduled task is directly destroyed?

They aren't destroyed. As I said above, an attempt is made to interrupt the task. That's all.

So how could I then delay the shutdown if a @Scheduled method has not yet finished, for example a batch-updating database flow in a long-running for-each loop inside the scheduled method?

If the task ignores the thread being interrupted, it will continue running. Here's an example:

@Scheduled(fixedRate = 100000000L)
public void scheduled() {
    LOGGER.info("Starting scheduled job...");
    long end = System.currentTimeMillis() + (5 * 60 * 60 * 1000);
    while (System.currentTimeMillis() < end) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
            // Reset interrupt flag and keep going
            Thread.currentThread().interrupt();
        }
    }
    LOGGER.info("Scheduled job finished.");
}

The app will now wait for 30 seconds for the task to complete:

2022-04-06 12:46:24.319  INFO 60547 --- [           main] com.example.demo.Gh30556Application      : Started Gh30556Application in 1.685 seconds (JVM running for 2.031)
2022-04-06 12:46:33.151  INFO 60547 --- [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown        : Commencing graceful shutdown. Waiting for active requests to complete
2022-04-06 12:46:33.161  INFO 60547 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown        : Graceful shutdown complete
2022-04-06 12:47:03.170  WARN 60547 --- [ionShutdownHook] o.s.s.c.ThreadPoolTaskScheduler          : Timed out while waiting for executor 'taskScheduler' to terminate

If you would like ScheduledAnnotationBeanPostProcessor to behave differently so that running scheduled tasks are not interrupted, please open a Spring Framework issue.

Comment From: membersound

Thanks for the insight. So it's basically a problem of my test scenario, not the functionality itself?

I changed the test method as follows, then the shutdown is delayed correctly:

public void scheduled() {
    LOGGER.info("Starting scheduled job...");
    do { } while (true);
}

Comment From: wilkinsona

So it's basically a problem of my test scenario, not the functionality itself?

Yes.

If you have any further questions, please follow up on Stack Overflow or Gitter. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.

Comment From: isaac-nygaard-iteris

The solution given, where we can add the following...

try {
    ...
} catch (InterruptedException ex) {
    // Reset interrupt flag and keep going
    Thread.currentThread().interrupt();
}

... to code that throws those exceptions does not work. Sending SIGTERM still causes the application to close before thead pool tasks have finished running.

Comment From: jhkim-grip

any changes? it seem spring project define it bug and fix it though https://github.com/spring-projects/spring-framework/issues/31019