In our our background library we create our triggers via factory methods
public Trigger buildTrigger(final JobDescription jobDescription) {
final JobDescription.TriggerType triggerType = jobDescription.getTriggerType();
if (triggerType == null) {
throw new IllegalArgumentException("trigger type can not be empty");
}
return switch (triggerType) {
case CRON -> makeCronTrigger(jobDescription);
case ONCE -> makeOnceTrigger(jobDescription);
case MULTIPLE_TIMES -> makeMultipleTimesTrigger(jobDescription);
case LOOP -> makeLoopTrigger(jobDescription);
};
}
private CronScheduleBuilder createCronSchedule(JobDescription jobDescription) {
CronScheduleBuilder cronSchedule = null;
MisfirePolicy misfirePolicy = jobDescription.getMisfirePolicy() != null ? jobDescription.getMisfirePolicy() : MisfirePolicy.None;
switch (misfirePolicy) {
case DoNothing:
cronSchedule = cronSchedule(jobDescription.getCronExpression()).withMisfireHandlingInstructionDoNothing();
break;
case FireAndProceed:
cronSchedule = cronSchedule(jobDescription.getCronExpression()).withMisfireHandlingInstructionFireAndProceed();
break;
case IgnoreMisfires:
cronSchedule = cronSchedule(jobDescription.getCronExpression()).withMisfireHandlingInstructionIgnoreMisfires();
break;
default:
cronSchedule = cronSchedule(jobDescription.getCronExpression());
}
return cronSchedule;
}
private Trigger makeCronTrigger(final JobDescription jobDescription) {
final TriggerBuilder<CronTrigger> cronTriggerTriggerBuilder = newTrigger()
.withIdentity(jobDescription.getJobId())
.withSchedule(createCronSchedule(jobDescription))
.withPriority(getPriorityValue(jobDescription));
setIdentity(jobDescription, cronTriggerTriggerBuilder);
return cronTriggerTriggerBuilder.build();
}
in our functional micro service we build our jobs that it will utilize the trigger
@Bean
@ConditionalOnProperty(prefix = "tickets.background.job", name = "deliver-tickets.enabled", havingValue = "true")
JobDescription deliverTicketsJobDescription() {
final long now = metrics ? System.currentTimeMillis() : 0;
JobDescription jobDescription = JobDescriptionBuilder.builder()
.triggerType(TriggerType.CRON)
.jobClass(DeliverTicketsJob.class)
.cronExpression(deliverTicketsCron) // 0/5 * * * * ?
.misfirePolicy(MisfirePolicy.FireAndProceed)
.jobGroupId(jobGroupId)
.jobId("deliverTickets")
.build();
if (metrics) {
LOGGER.debug("METRIC: deliverTicketsJobDescription ~ duration: " + (System.currentTimeMillis() - now) + "ms");
}
return jobDescription;
}
We also set the misfireThreshold to 1 second
public class TicketApplication {
static {
System.setProperty("quartz.jobStore.misfireThreshold", "1000");
}
I have verified via looking at the qrtz_triggers table and seeing our job and the misfire_instruction column set.
however from my point of view the behavior never changes in regards to handling misfires, it is always:
to me these steps illustrate the handling of the misfires. the misfires from when it starts up (from the time it was down), and from when a job takes longer then the interval). The executing of the misfired jobs should be controlled via the MisfireHandlingInstruction
assigned, but like I said it has no effect on the outcome from the steps listed above and I tried them all.
I have since reverted quartz.jobStore.misfireThreshold. reworked the job so that its manually triggered when there is an add/update operation. Which enabled me to reconfigure the interval to be 2 minutes instead of 5 seconds. with it configured this way we do see slight variations, so i guess it is working.....