Closed s-evgheni closed 4 months ago
Furthermore there seems to be a bug in the framework which will allow you to perform dynamic CRON scheduling if you manage to schedule a delayed task before it with the same set of primary keys. Like this:
@GetMapping(value = "/dbs-enque-cron-job", produces = {MediaType.TEXT_PLAIN_VALUE})
public String dbsNewCronJob(@RequestParam(value = "cron_expression", defaultValue = "0 */1 * * * *") String cronExpression) {
log.info("Provided cron value:" + cronExpression);
//get task definition
Task<PlainScheduleAndData> task = taskConfiguration.dynamicCronTask();
//define run schedule
Schedule cron = Schedules.cron(cronExpression);
PlainScheduleAndData when = new PlainScheduleAndData(cron);
//create instance based on task definition and run schedule
String randomTaskInstanceId = UUID.randomUUID().toString();
SchedulableInstance<?> taskInstanceOne = task.schedulableInstance(randomTaskInstanceId, new PlainScheduleAndData(Schedules.fixedDelay(Duration.ofSeconds(1))));
SchedulableInstance<?> taskInstanceTwo = task.schedulableInstance(randomTaskInstanceId, when);
//schedule instance for recurring execution (e.g: add to Postgres scheduled_tasks table)
//1st add serializable FixedDelay task instance
this.schedulingService.getDbScheduler().schedule(taskInstanceOne);
//2nd override FixedDelay task instance with a cron expression
this.schedulingService.getDbScheduler().schedule(taskInstanceTwo);
return "Recurring Job enqueued with an id of: " + taskInstanceTwo.getId() + " and cron schedule:" + when.getSchedule().toString();
}
So essentially in this scenario we will end up with running taskInstanceTwo logic on taskInstanceOne schedule.
Anther problem that I'd encountered is the ability to schedule the same recurring logic as many times as you want.
For example if I define a bean like this in the configuration:
@Bean
public RecurringTask<Void> cronTask() {
Schedule cron = Schedules.cron("0 */2 * * * *");
return Tasks.recurring("auto-cron-task", cron)
.execute((taskInstance, executionContext) -> {
log.info("Running auto-cron-task... Instance: '" + taskInstance.getId());
});
}
And a simple GET route like this:
@GetMapping(value = "/enque-cron", produces = {MediaType.TEXT_PLAIN_VALUE})
public String dbsNewCronJob() {
RecurringTask<Void> task = taskConfiguration.cronTask();
//create instance based on task definition and run schedule
String randomTaskInstanceId = UUID.randomUUID().toString();
SchedulableInstance<?> taskInstanceOne = task.schedulableInstance(randomTaskInstanceId);
this.schedulingService.getDbScheduler().schedule(taskInstanceOne);
return "Recurring Job enqueued with an id of: " + taskInstanceOne.getId();
}
Two things will happen.
I was under assumption that the library should prevent this from happening and make sure that only a singe copy of the same instance can be marked for execution at any given time. Further attempts to add the same task should be prevented, but maybe I'm just doing something silly here. The documentation is not very clear on the best practices with your library so I'm just building on a lot of my own assumptions at this point.
@kagkarlsson your feedback will be greatly appreciated :)
Yeah, currently I suppose CronSchedule
is not Serializable, it probably should be! Ideally, only the cron-pattern string is serialized. You could copy the CronSchedule and adapt as needed.
//1st add serializable FixedDelay task instance
this.schedulingService.getDbScheduler().schedule(taskInstanceOne);
//2nd override FixedDelay task instance with a cron expression
this.schedulingService.getDbScheduler().schedule(taskInstanceTwo);
The behavior for client.schedule(..)
is currently that it will not overwrite. It could be enhanced with explicit methods that actually will replace any existing. A workaround should be to cancel
the existing execution first.
So essentially in this scenario we will end up with running taskInstanceTwo logic on taskInstanceOne schedule.
The logic is the same. It is using the same task.
@GetMapping(value = "/enque-cron", produces = {MediaType.TEXT_PLAIN_VALUE})
public String dbsNewCronJob() {
RecurringTask<Void> task = taskConfiguration.cronTask();
//create instance based on task definition and run schedule
String randomTaskInstanceId = UUID.randomUUID().toString();
SchedulableInstance<?> taskInstanceOne = task.schedulableInstance(randomTaskInstanceId);
this.schedulingService.getDbScheduler().schedule(taskInstanceOne);
return "Recurring Job enqueued with an id of: " + taskInstanceOne.getId();
}
"Static" recurring tasks should not be scheduled explicitly, it will be handled by the scheduler (which then guarantees a single instance). The problem here is that it is scheduled explicitly with multiple unique instance-ids. If you want to do that, you need a "dynamic" recurring task.
@kagkarlsson I was looking at your library, trying to figure out how would one go about creating dynamic CRON task ?
My current understanding is that due to the fact that CronSchedule is not Seriallizable it is not a good fit for this kind of operation.
Here is what I was trying to achieve.
In a SpringBoot app define task configuration such us:
Try to schedule the task above from a Spring controller:
Outcome: