Reschedule the scheduled task in spring scheduler - spring

I have a Runnable class which is scheduled to run at fixed rate (I am using spring scheduler):
taskScheduler.scheduleAtFixedRate(this, startTime.toDate(),
PERIOD * 1000);
Can I reschedule the task or change the period dynamically after it has been scheduled?

You can probably use the following DynamicPeriodicTrigger https://github.com/spring-projects/spring-integration-samples/blob/master/intermediate/dynamic-poller/src/main/java/org/springframework/integration/samples/poller/DynamicPeriodicTrigger.java
So you have to create a DynamicPeriodicTrigger instance (with fixedRate = true) and put the reference into taskScheduler.schedule(Runnable task, Trigger trigger); method.
You can than dynamically change object attributes, to change execution periods on the fly.

Related

how to prevent quartz from running another job if the previous one is still running?

I'm using Quarkus. My Quartz jobs are scheduled to run every 10 seconds:
return TriggerBuilder.newTrigger()
.withIdentity("my-job")
.startNow()
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.repeatForever()
).build();
This works fine but jobs keep triggering every 10 seconds irrespective of whether or not the last one finishes. I need the next job to start only if there are no jobs currently running job. How do I accomplish this?
Add #DisallowConcurrentExecution on you Job class.
as example :
#DisallowConcurrentExecution
public class MyScheduledJob implements Job {
//execution method
}

Updating a QuartzJob from the running job itself

The update of a QuartzJob within a spring boot application works while the job is not running (here or here). The spring variable spring.quartz.overwrite-existing-jobs: true is set.
However, when doing the same from within a running job the job keeps firing itself in an endless loop without taking into account the interval time (each few milliseconds it fires again). I even tried doing the same from within a TriggerListener but that doesn't change it.
As code example I would have nothing else but what is given in the second link above:
// retrieve the trigger
Trigger oldTrigger = sched.getTrigger(triggerKey("oldTrigger", "group1");
// obtain a builder that would produce the trigger
TriggerBuilder tb = oldTrigger.getTriggerBuilder();
// update the schedule associated with the builder, and build the new trigger
// (other builder methods could be called, to change the trigger in any desired way)
Trigger newTrigger = tb.withSchedule(simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(10)
.build();
sched.rescheduleJob(oldTrigger.getKey(), newTrigger);
Did anyone try that from within a running job?
It works with the following trigger. It is the startAt which makes the difference. Without that the trigger fires immediately again.
Trigger trigger = newTrigger()
.withIdentity(triggerName, groupname)
.startAt(Date.from(LocalDateTime.now().plusSeconds(intervalInSeconds).atZone(ZoneId.systemDefault()).toInstant()))
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(intervalInSeconds)
.repeatForever()
.withMisfireHandlingInstructionIgnoreMisfires())
.build();

Quartz .NET - Prevent parallel Job Execution

I am using Quartz .NET for job scheduling.
So I created one job class (implementing IJob).
public class TransferData : IJob
{
public Task Execute(IJobExecutionContext context){
string tableName = context.JobDetail.JobDataMap.Get("table");
// Transfer the table here.
}
}
So I want to transfer different and multiple tables. For this purpose I am doing something like this:
foreach (Table table in tables)
{
IJobDetail job = JobBuilder.Create<TransferData>()
.WithIdentity(new JobKey(table.Name, "table_transfer"))
.UsingJobData("table", table.Name)
.Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity(new TriggerKey("trigger_" + table.Name, "table_trigger"))
.WithCronSchedule("*/5 * * * *")
.ForJob(job)
.Build();
await this.scheduler.ScheduleJob(job, trigger);
}
So every table should be transfered every 5 minutes. To achieve this I create several jobs with different job names.
The question is: how to prevent the parallel job execution for the same jobName? (e.g. the previous run takes longer for one table, so I do not want to start the next transfer for the same table.)
I know about the attribute #DisallowConcurrentExecution, but this is used to prevent the parallel execution for the same Job class. I do not want to write an extra Job class per table, because the "main" code for the transfer is always the same, the one and only difference is the table name. So I want to use the same job class for this purpose.
The Quatz .NET documentation is a little bit confusing.
DisallowConcurrentExecution is an attribute that can be added to the
Job class that tells Quartz not to execute multiple instances of a
given job definition (that refers to the given job class)
concurrently. Notice the wording there, as it was chosen very
carefully. In the example from the previous section, if
“SalesReportJob” has this attribute, than only one instance of
“SalesReportForJoe” can execute at a given time, but it can execute
concurrently with an instance of “SalesReportForMike”. The constraint
is based upon an instance definition (JobDetail), not on instances of
the job class. However, it was decided (during the design of Quartz)
to have the attribute carried on the class itself, because it does
often make a difference to how the class is coded.
Source: https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/more-about-jobs.html
But if you read the API documentation, it's says: the bold text is important!
An attribute that marks a IJob class as one that must not have
multiple instances executed concurrently (where instance is based-upon
a IJobDetail definition - or in other words based upon a JobKey).
Source: https://quartznet.sourceforge.io/apidoc/3.0/html/
In other words: the DisallowConcurrentExecution attribute works for my purposes.

Spring Scheduler stops working for my cron expression

I've a method scheduled to run periodically with Spring Scheduler, it's been working fine and stopped working today with no error. What could be the potential cause ? Is there any alternative way to schedule task periodically using Spring Scheduler that ensures that the method will be executed no matter what?
#Scheduled(cron="0 0/1 * * * ?")
public void executePollingFlows(){
if(applicationConfig.isScheduleEnabled()) {
for (long flowId : applicationConfig.getPollingFlowIds()) {
flowService.executeFlow(flowId);
}
logger.info("Finished executing all polling flows at {}", new Date());
}
}
You may have got Out of Memory exception if the job could not finish its tasks but you try to run it again and again. If it is a Out of Memory exception you may try to create a ThreadPool and check it in every run. If there is no enough space in the ThreadPool you can skip the task for this turn.
There is alternative way to use #Scheduled periodically. You may change your #Scheduled annotation with this:
#Scheduled(fixedRate=1000)
It will still be running in every second and if necessary you can add initialDelay to it:
#Scheduled(initialDelay=1000, fixedRate=1000)
You can find more details about fixedRate, initialDelay and fixedDelay here:
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html

Allow the end user to schedule task with Spring Boot

I'm using Spring Boot and I want to allow the end user to schedule task as he wants.
I have a Spring Boot REST backend with a Angular frontend. The user should be able to schedule (with a crontab style syntax) a task (i.e. a class' method backend side), and choose some arguments.
The user should alse be able to view, edit, delete scheduled task from the front end
I know I can use #Scheduled annotation, but I don't see how the end user can schedule task with it. I also take a look at Quartz, but I don't see how to set the user's argument in my method call.
Should I use Spring Batch?
To schedule job programatically, you have the following options:
For simple cases you can have a look at ScheduledExecutorService, which can schedule commands to run after a given delay, or to execute periodically. It's in package java.util.concurrent and easy to use.
To schedule Crontab job dynamically, you can use quartz and here's official examples, basically what you'll do is:
Create instance Scheduler, which can be defined as a java bean and autowire by Spring, example code:
Create JobDetail
Create CronTrigger
Schedule the job with cron
Official example code(scheduled to run every 20 seconds):
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
JobDetail job = newJob(SimpleJob.class)
.withIdentity("job1", "group1")
.build();
CronTrigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(cronSchedule("0/20 * * * * ?"))
.build();
sched.scheduleJob(job, trigger);

Resources