-
Notifications
You must be signed in to change notification settings - Fork 38.6k
Closed
Labels
in: coreIssues in core modules (aop, beans, core, context, expression)Issues in core modules (aop, beans, core, context, expression)type: regressionA bug that is also a regressionA bug that is also a regression
Milestone
Description
Since Spring 6.2.0
functionality to specify custom scheduler for @Scheduled
annotation is not working anymore.
Minimal reproducible example:
import static org.assertj.core.api.Assertions.assertThat;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.SimpleAsyncTaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskHolder;
import org.springframework.scheduling.config.TaskSchedulerRouter;
class SchedulerConfigTest {
@Test
void withQualifiedScheduler() throws Exception {
var ctx = new AnnotationConfigApplicationContext(QualifiedExplicitSchedulerConfig.class);
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(2);
Thread.sleep(110);
assertThat(ctx.getBean("defaultSchedulerThreads", Set.class))
.hasSizeGreaterThanOrEqualTo(1).allMatch(e -> ((String) e).startsWith("taskScheduler-"));
assertThat(ctx.getBean("explicitSchedulerThreads", Set.class))
.hasSizeGreaterThanOrEqualTo(1).allMatch(e -> ((String) e).startsWith("customScheduler-"));
}
@TestConfiguration
@EnableScheduling
static class QualifiedExplicitSchedulerConfig {
public static final String DEFAULT_TASK_SCHEDULER_BEAN_NAME = TaskSchedulerRouter.DEFAULT_TASK_SCHEDULER_BEAN_NAME;
public static final String CUSTOM_TASK_SCHEDULER_BEAN_NAME = "customTaskScheduler";
@Bean
public Set<String> defaultSchedulerThreads() {
return new HashSet<>();
}
@Bean
public Set<String> explicitSchedulerThreads() {
return new HashSet<>();
}
@Bean(name = DEFAULT_TASK_SCHEDULER_BEAN_NAME)
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setThreadNamePrefix("taskScheduler-");
return taskScheduler;
}
@Bean(name = CUSTOM_TASK_SCHEDULER_BEAN_NAME)
public SimpleAsyncTaskScheduler customTaskScheduler() {
SimpleAsyncTaskScheduler taskScheduler = new SimpleAsyncTaskScheduler();
taskScheduler.setThreadNamePrefix("customScheduler-");
return taskScheduler;
}
@Scheduled(fixedRate = 10)
public void task() throws Exception {
defaultSchedulerThreads().add(Thread.currentThread().getName());
}
@Scheduled(fixedRate = 10, scheduler = CUSTOM_TASK_SCHEDULER_BEAN_NAME)
public void taskWithExplicitScheduler() throws Exception {
explicitSchedulerThreads().add(Thread.currentThread().getName());
}
}
}
I believe this happened in this commit dc2c8d60. Now Runnable
is wrapped into OutcomeTrackingRunnable
. As result TaskSchedulerRouter
fails to determine qualifier:
protected String determineQualifier(Runnable task) {
return (task instanceof SchedulingAwareRunnable sar ? sar.getQualifier() : null);
}
since OutcomeTrackingRunnable
is not implementing SchedulingAwareRunnable
.
@bclozel, do you think it makes sense for OutcomeTrackingRunnable
to implement SchedulingAwareRunnable
interface instead of Runnable
? Or to have two flavours of OutcomeTrackingRunnable
? And Task
will wrap underlying runnable depends on underlying task?
Smth like:
public Task(Runnable runnable) {
Assert.notNull(runnable, "Runnable must not be null");
if (runnable instanceof SchedulingAwareRunnable sar) {
this.runnable = new OutcomeTrackingSchedulingAwareRunnable(runnable);
} else {
this.runnable = new OutcomeTrackingRunnable(runnable);
}
this.lastExecutionOutcome = TaskExecutionOutcome.create();
}
Metadata
Metadata
Assignees
Labels
in: coreIssues in core modules (aop, beans, core, context, expression)Issues in core modules (aop, beans, core, context, expression)type: regressionA bug that is also a regressionA bug that is also a regression