For my application I create jobs and schedule them with CronTriggers. Each job has only one trigger and both the job name and the trigger names are the same. No jobs share a trigger.
Now when i create a cron trigger like this "0/1 * * * * ?" which instructs the job to execute every second, it works just fine.
The problem rises when I first pause the job by calling:
scheduler.pauseJob(jobName, jobGroup);
and then resuming the job after let's say 50 seconds with:
scheduler.resumeJob(jobName, jobGroup);
What I see is that for these 50 seconds the job did not execute as requested. But the moment I resume the job I see 50 executions of the job at the same time!!!
I thought that this was due to the default setting for the misfire instruction but even after setting the trigger's misfire instruction upon creation to this:
trigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
The same thing happens. Can anyone suggest a way to fix this?
The CronTrigger works by remembering the nextFireTime. After creating the trigger the nextFireTime is initialized. Every time the job is triggered nextFireTime is updated. Since the job is not triggered when paused nextFireTime remains "old". So after you resume the job the trigger will return every old trigger time.
The problem is, the trigger doesn't know it is being paused. To overcome this there is this misfire handling. After resuming the jobs the trigger's updateAfterMisfire() method will be invoked which corrects the nextFireTime. But not if the difference between nextFireTime and now is smaller than the misfireThreshold. Then the method is never called. This threshold's default value is 60,000. Thus if your pause period would be longer than 60s everything would be fine.
Since you have problems I assume it is not. ;)
To workaround this you can modify the threshold or use a simple wrapper around CronTrigger:
public class PauseAwareCronTrigger extends CronTrigger {
// constructors you need go here
#Override
public Date getNextFireTime() {
Date nextFireTime = super.getNextFireTime();
if (nextFireTime.getTime() < System.currentTimeMillis()) {
// next fire time after now
nextFireTime = super.getFireTimeAfter(null);
super.setNextFireTime(nextFireTime);
}
return nextFireTime;
}
}
If you pause the job, the trigger will continue to fire, but the executions will queue up until the job is resumed. This isn't a misfiring trigger, so that setting will have no effect.
What you want to do, I think, is programmatically disable or remove the cron trigger, rather than pausing the job. When you want to resume, then re-add the trigger.
Since 1.6.5 at least (the earliest version of quartz at my fingertips), the scheduler has a pauseTrigger method that takes the name/group as parameters. This means you don't have to have a sub-class of every trigger type you use, nor do you have to do funky deletion/insertion tricks.
Both of these are important to me because 1) our database has a strict no-deletes policy and 2) the custom datastore I use doesn't support trigger sub-classes.
you can add these code in org.quartz.impl.jdbcjobstore.JobStoreSupport#resumeTrigger(Connection conn, TriggerKey key)
OperableTrigger trigger = getDelegate().selectTrigger(conn, key);
if (trigger.getNextFireTime().getTime() < System.currentTimeMillis()) {
trigger.setNextFireTime(trigger.getFireTimeAfter(null));
}
JobDetail job = retrieveJob(conn, status.getJobKey());
storeTrigger(conn, trigger, job, true, status.getStatus(), false, false);
Using these code, when the paused job be resumed it will not be fired at once.On the other hand, it will be fired at next fire time which is calculated by resumed time
Related
In my Spring program, I have a Scheduled task.
#Scheduled(cron = "0 0 0 2 * *") // hardcoded schedule
public void executeBatchJob() {
batchJob.execute();
}
I have a specification change and now have to let the user freely configure the date and time of execution via an API.
One way I came up was to run a scheduled task every morning at 0:00 and check if the date is indeed the date of execution. If true, check the time of execution and schedule the batch job to run at that time of the day.
Is there a "Spring" way of achieving this?
Triggers can be used to configure the scheduled jobs.
From the docs
The basic idea of the Trigger is that execution times may be determined based on past execution outcomes or even arbitrary conditions.
Check out this answer for detailed explanation.
I am creating a JobDetail as below,
JobDetail job = JobBuilder.newJob(MyJob.class).withIdentity(queryId).usingJobData("ExecutionDelay",1).build();
I want it to first run after 1 hour.
The Trigger for the same is as below,
Trigger trigger = TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInHours(1)).build();
But, when I am creating more than 1 Thread of the above code in a class, the Job gets triggered immediately for some of the Jobs.
Not sure what is wrong. Can some one help.
It is most likely because you do not set the start time when you create your trigger. If you do not set the start time explicitely, then the current time (new Date()) is assumed and then depending on the your trigger's misfire instruction (default is SMART_POLICY) and timing, the job may be fired immediately.
To solve the problem, you will need to set the trigger's start time to "new Date() + 1 hour".
I am using quart library for scheduling in java. I have written a trigger like this
trigger = newTrigger().withIdentity("myTrigger", "myGroup").startNow() .withSchedule(cronSchedule(croneExpression).withMisfireHandlingInstructionFireAndProceed()) .forJob("myJob","myGroup") .build();
Now when i set the scheduler with cronexpression which is dynamically generated scheduler runs properly. But when server is shutdown during the period in which scheduler is set to fire , the values in quartz_trigger i.e. the next_fire_time in table is changed after the execution time of the job.Because of this the misfire of the scheduler do not work.So my purpose is not solved.So what can be the problem? Is any property in the quartz.properties to be set. I am not using job.xml(jobInitializer) to set the scheduler.
the values in quartz_trigger i.e. the next_fire_time in table is changed after the execution >time of the job
Yes, this is quartz actually does in case of misfires.
As per the misfire instruction provided while creating a trigger, quartz calculates how many times the misfired execution has to be executed.
As per your code , you have set the misfire instruction as "fireAndProceed" , So Quartz just executes the very first misfired execution ( and neglect all the subsequent remaining misfires). Ex: If you set the trigger to fire between 2Pm to 4pm with interval of 30 min and if scheduler was down during 2:29pm to 3.29pm, then only one trigger execution of time 2.30pm
will be executed ( and executions of 3.pm will be neglected).
Hope this answers your question. :-)
I have a simpleTrigger defined. The repeatInterval is 10 seconds.
I noticed at some situation, after one event fired, it immediately fired another one (not 10 seconds later), 3rd even fired at 10 seconds later.
Any idea about what happened, and how to correct this behavior?
I don't know whether it is a mis-fire situation (I didn't see the error), if it is how to correct this behaviour, I don't want the event fired immediately after that one.
When running the program I see the job fired at 1365178800586 milliseconds.
then fired at 1365178800593 milliseconds, ie at appoximately the same time.
subsequently fired at 1365178810583 milliseconds, ie at approximately 10 seconds after the first call.
Java code snippet:
String testJobname = "testJob";
JobDetail testJobDetail = new JobDetail();
testJobDetail.setGroup(this.getClass().getName());
testJobDetail.setName(testJobname);
testJobDetail.setJobClass(TestJob.class);
final JobDataMap data = new JobDataMap();
data.put(CheckLimits.class.getName(), checkLimitsCmd);
testJobDetail.setJobDataMap(data);
Trigger testTrigger = new SimpleTrigger(testJobDetail.getName(),
testJobDetail.getGroup(), SimpleTrigger.REPEAT_INDEFINITELY,
10000);
First off, I think you should be using TriggerBuilder and ScheduleBuilder instead of instantiating SimpleTrigger directly.
Schedule schedule = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).build();
Trigger trigger = TriggerBuilder.newTrigger().forJob(testJobDetail).usingJobData(data).withSchedule(schedule).build();
The Quartz documentation have some good examples in the Javadoc code, check out this for more information -
http://quartz-scheduler.org/api/2.0.0/org/quartz/SimpleScheduleBuilder.html
I want to get the delay between the time a job was supposed to execute and the time it actually executed for the most recent execution. For example, if a job was supposed to fire at 8pm and it actually fired at 8.10pm, the result should be 10 minutes.
I know that I can use Trigger.getPreviousFireTime() to get the last time it actually executed, but I can't see any way to get the corresponding scheduled time (e.g. 8pm for the example above), is this possible?
Inside your job use the following code:
#Override
public void execute(final JobExecutionContext context) {
long diffInMillis =
new Date().getTime() - context.getScheduledFireTime().getTime();
//...
}
As you probably guessed context.getScheduledFireTime() is the time when job was suppose to run (ideally the difference should be close to 0).
Note that if your job is late more than 10 minutes (configurable) it might not fire at all - it depends on your misfire instruction set up.