I'm facing a painful problem with Quartz and misfires.
My App creates allows user to create CronTrigger and SimpleTrigger Jobs.
Each Job can be paused/resumed (using Scheduler.pauseJob and Scheduler.resumeJob)
The scheduler itself can be set to standby.
We'd like to discard any misfires :
As explained in this blog post http://www.nurkiewicz.com/2012/04/quartz-scheduler-misfire-instructions.html, I've tried
but none could discard misfires.
I'm currently using an ugly workaround in job execute method:
public void execute(JobExecutionContext context) throws JobExecutionException {
Date dateNow = new Date();
long diff = dateNow.getTime() - context.getScheduledFireTime().getTime();
if (diff > 500)
{
//its a misfire
return;
}
/* rest of job execution code */
When scheduledFireTime is more than 500ms older than now, discard it.
But it seems that sometimes, in production, this allows some jobs to be executed (it has been reported that occurs when app is restarted)
Could that be possible ? Is there any pretty way to avoid misfires ?
Quartz version : 2.1.7 (In a Spring 3.2.5 app)
quartz.properties
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.dataSource=psqldatasource
org.quartz.dataSource.psqldatasource.driver=${db.driver}
org.quartz.dataSource.psqldatasource.URL=${db.url}
org.quartz.dataSource.psqldatasource.user=${db.usr}
org.quartz.dataSource.psqldatasource.password=${db.pwd}
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
for Cron :
JobDetail job = JobBuilder.newJob(JobLauncher.class)
.withIdentity(jobIdentifierBean.getNameJob(), jobIdentifierBean.getNameGroup())
.usingJobData(...) //defining some jobData key/values
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(name, group)
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(strCronExpression).withMisfireHandlingInstructionDoNothing())
.build();
try {
scheduler.scheduleJob(job, trigger);
} catch (SchedulerException e) {
throw e;
}
for SimpleTrigger (job is defined as in cron case)
//avoid first fire
GregorianCalendar g = new GregorianCalendar();
switch (enumDelayUnits) {
case Days:
g.add(GregorianCalendar.HOUR, delay * 24);
break;
case Hours:
g.add(GregorianCalendar.HOUR, delay);
break;
case Minutes:
g.add(GregorianCalendar.MINUTE, delay);
break;
case Seconds:
g.add(GregorianCalendar.SECOND, delay);
break;
default:
throw new ServiceException("Unknow delay type");
}
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobIdentifierBean.getNameTrigger(), jobIdentifierBean.getNameGroup())
.startAt(g.getTime())
.withSchedule(getSimpleScheduleBuilder(delay, enumDelayUnits, repeatForever))
.build();
try {
scheduler.scheduleJob(job, trigger);
} catch (SchedulerException e) {
throw e;
}
private SimpleScheduleBuilder getSimpleScheduleBuilder(int delay, EnumDelayUnits enumDelayType, boolean repeatForever) throws ServiceException, Exception
{
SimpleScheduleBuilder simpleScheduleBuilder;
simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
switch (enumDelayType) {
case Days:
simpleScheduleBuilder.withIntervalInHours(24 * delay);
break;
case Hours:
simpleScheduleBuilder.withIntervalInHours(delay);
break;
case Minutes:
simpleScheduleBuilder.withIntervalInMinutes(delay);
break;
case Seconds:
simpleScheduleBuilder.withIntervalInSeconds(delay);
break;
default:
serviceError("Unknown delay " + enumDelayType);
}
if(repeatForever)
{
simpleScheduleBuilder.repeatForever();
}
simpleScheduleBuilder = simpleScheduleBuilder.withMisfireHandlingInstructionNextWithRemainingCount();
return simpleScheduleBuilder;
}
A misfire occurs if a persistent trigger “misses” its firing time because of the scheduler being shutdown, or because there are no available threads in Quartz's thread pool for executing the job. The different trigger types have different misfire instructions available to them.
Trigger - a component that defines the schedule upon which a given Job will be executed. JobBuilder - used to define/build JobDetail instances, which define instances of Jobs.
I am not sure if you ever figured this out, but I had a similar issue and the solution for me was related to the "misfireThreshold" in my configuration:
quartz.jobStore.misfireThreshold
Mine was set to 60000 (aka 1 minute) and so upon restarting my scheduling service, if they were within a minute of being "late", they still executed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With