Reputation: 71
I have a simple scheduled task that was created using the @Scheduled annotation. Something like this -
public void doSomething() {
// My code
}
I also have a blue-green upgrade scenario where this scheduled task may potentially run in both the blue and green clusters. And given this scheduled task modifies the DB, there is a possibility that one of the nodes might overwrite the data from the other node - or worst case race conditions.
I want to pause all the scheduled tasks on the green cluster until it is ready to accept traffic. I already have the code wired to listen to changes to the application state.
I explored a few options -
The problem with these approaches is they skip the task for that particular schedule and not pause it. So, if the task is skipped, say at T, and the application becomes immediately, the application will have to wait T+30mins to refresh data.
Is there a way in springboot to pause schedulers and resume immediately?
Upvotes: 4
Views: 2350
Reputation: 9676
Customize the task scheduler to wait until it finishes the job and allow spring boot to do the graceful shutdown
@Bean
TaskSchedulerCustomizer taskSchedulerCustomizer() {
return taskScheduler -> {
taskScheduler.setAwaitTerminationSeconds(60);
taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
};
}
another opinion
Use Shedlock to lock the scheduler for the given amount of time(upgradable time). It also solves the scheduler running on all/multiple instances instead of one instance.
Upvotes: 0
Reputation: 2935
It looks like natively spring does not have the ability to pause scheduled tasks. I tend to use Quartz Scheduler when running schedule tasks with spring.
In order to do that we have to do a couple configurations.
First we have to setup our context aware component:
@Component
public final class ApplicationContextHolder extends SpringBeanJobFactory implements ApplicationContextAware {
private static ApplicationContext context;
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
if (context == null) {
beanFactory = ctx.getAutowireCapableBeanFactory();
context = ctx;
}
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
/**
* Get the current application context
* @return the current context
*/
public static ApplicationContext getContext() {
return context;
}
}
Then our configuration:
@Configuration
public class QuartzSchedulerConfig {
@Autowired
private ApplicationContext applicationContext;
/**
* Create the job factory bean
* @return Job factory bean
*/
@Bean
public JobFactory jobFactory() {
ApplicationContextHolder jobFactory = new ApplicationContextHolder();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
/**
* Create the Scheduler Factory bean
* @return scheduler factory object
*/
@Bean
public SchedulerFactoryBean schedulerFactory() {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setAutoStartup(true);
factory.setSchedulerName("Scheduler");
factory.setOverwriteExistingJobs(true);
factory.setJobFactory(jobFactory());
return factory;
}
}
Then our actual service:
@Service
public class SchedulerService {
@Autowired
private SchedulerFactoryBean schedulerFactory;
private Scheduler scheduler;
private ScheduledExecutorService executor;
/**
* Initialize the scheduler service
*/
@PostConstruct
private void init() {
scheduler = schedulerFactory.getScheduler();
executor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
}
...//Other Scheduling tasks can go here
public void pauseJob(String triggerName, String groupName) {
TriggerKey tk = new TriggerKey(triggerName, groupName);
scheduler.pauseJob(tk);
}
}
Quartz Scheduling gives a lot of flexibility when it comes to scheduling tasks
http://www.quartz-scheduler.org/overview/
Upvotes: 2