prashant
prashant

Reputation: 71

How to pause a scheduled task started using springboot's @Scheduled annotation?

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 -

  1. Simply adding a boolean at the start of the task.
  2. Adding a decorator to the ThreadPoolTaskScheduler.

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

Answers (2)

Thirumal
Thirumal

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

locus2k
locus2k

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

Related Questions