Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

CronTrigger support for overlapping executions #22937

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in 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