Skip to content

CronTrigger support for overlapping executions #22937

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
dydeve opened this issue May 9, 2019 · 1 comment
Closed

CronTrigger support for overlapping executions #22937

dydeve opened this issue May 9, 2019 · 1 comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: declined A suggestion or change that we don't feel we should currently apply

Comments

@dydeve
Copy link

dydeve commented May 9, 2019

As mentioned in SPR-10556, overlapping executions won't occur.

But in my project, I have a scheduled task which has to run every hour (with cron expression 0 20 * * * ?) .In most case, it took 20 to 30 minutes. But sometimes, it took 2 to 3 hours due to force majeure. Though there is a way to solve it by submitting the task to a separate thread pool, it would be better if spring-context support overlapping executions.

For now, I solve it by the following codes:

  1. BaseOnLSETCronTrigger.java which copy from org.springframework.scheduling.support.CronTrigger but changed in
	/**
	 * Determine the next execution time according to the given trigger context.
	 * <p>Next execution times are calculated based on the
	 * {@linkplain TriggerContext#lastScheduledExecutionTime completion time} of the
	 * previous execution; therefore, overlapping executions will occur on purpose.
	 */
	@Override
	public Date nextExecutionTime(TriggerContext triggerContext) {
		Date date = triggerContext.lastScheduledExecutionTime();
		if (date == null) {
			date = new Date();
		}
		return this.sequenceGenerator.next(date);
	}
  1. ReschedulingImmediatelyRunnable.java copy from org.springframework.scheduling.concurrent.ReschedulingRunnable and changed in
        /**
	 * we firstly call schedule() and then call super.run() to keep
	 * tasks which take lots of time from delaying the schedule
	 */
	@Override
	public void run() {
		Date actualExecutionTime = new Date();

		this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, null);

		if (!obtainCurrentFuture().isCancelled()) {
			schedule();//schedule first
		}

		super.run();//then run
		Date completionTime = new Date();
		synchronized (this.triggerContextMonitor) {
			Assert.state(this.scheduledExecutionTime != null, "No scheduled execution");
			this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime);

		}
	}
  1. To apply ReschedulingImmediatelyRunnable.java to spring-context, I copy ReshdeulingImmediatelyTaskScheduler.java from org.springframework.scheduling.concurrent.ConcurrentTaskScheduler and changed in
	@Override
	@Nullable
	public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
		try {
			ErrorHandler errorHandler =
					(this.errorHandler != null ? this.errorHandler : TaskUtils.getDefaultErrorHandler(true));
			return new ReschedulingImmediatelyRunnable(task, trigger, this.scheduledExecutor, errorHandler).schedule();
		} catch (RejectedExecutionException ex) {
			throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
		}
	}
  1. And finally
@EnableScheduling
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {

	private static final Logger log = LoggerFactory.getLogger(ScheduleConfig.class);

	@Autowired
	private Scheduler scheduler;

	@Override
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {

		taskRegistrar.setScheduler(new ReshdeulingImmediatelyTaskScheduler(taskExecutor()));

		/**
		 * this task would took much time than expected, and it has to run every hour
		 */
		taskRegistrar.addTriggerTask(() ->
				scheduler.syncPlayStatistics()
		, new BaseOnLSETCronTrigger("0 20 * * * ? "));
	}
	
	@Bean(destroyMethod = "shutdown")
	public ScheduledExecutorService taskExecutor() {
		ScheduledThreadPoolExecutor executorService = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(4);
		log.info("executorService:{}", executorService);
		return executorService;
	}
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label May 9, 2019
@bclozel bclozel changed the title Can spring-context support overlapping executions? CronTrigger support for overlapping executions Apr 1, 2020
@rstoyanchev rstoyanchev added the in: core Issues in core modules (aop, beans, core, context, expression) label Nov 10, 2021
@jhoeller
Copy link
Contributor

This might be a good fit with SimpleAsyncTaskScheduler (#30956) which dispatches each cron-triggered task onto a separate thread, primarily targeting a Virtual Threads setup. Beyond that, we do not intend to support overlapping executions specifically.

@jhoeller jhoeller closed this as not planned Won't fix, can't repro, duplicate, stale Dec 29, 2023
@jhoeller jhoeller removed the status: waiting-for-triage An issue we've not yet triaged or decided on label Dec 29, 2023
@sbrannen sbrannen added the status: declined A suggestion or change that we don't feel we should currently apply label Dec 29, 2023
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: declined A suggestion or change that we don't feel we should currently apply
Projects
None yet
Development

No branches or pull requests

5 participants