javaquartz-scheduler

quartz cron expression trigger job not adhering to misfire policy


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:

  1. on startup, a job with misfires will fire off immediately and consecutively twice
  2. after that it will be back on schedule, in this case firing every 5 seconds
  3. i delay the job by 20 seconds, either via breakpoint or thread.sleep
  4. after resuming from delay, the job will be executed multiple times consecutively until it catches up
  5. at which point it goes back to being on schedule, every 5 seconds

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.


Solution

  • 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.....

    enter image description here

    enter image description here

    enter image description here

    enter image description here