Quartz Java resuming a job executes it many times

20,369

Solution 1

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;
    }
}

Solution 2

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.

Solution 3

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.

Share:
20,369
Mohammad Taha
Author by

Mohammad Taha

Android geek, TDD nut, International Speaker, ex Shazamer, currently the Principal Software Engineer for Apps at ASOS. Noti.st | Twitter | LinkedIn | GitHub

Updated on July 22, 2022

Comments

  • Mohammad Taha
    Mohammad Taha almost 2 years

    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?

  • Mohammad Taha
    Mohammad Taha over 14 years
    The javadoc for the pauseJob method says "Pause the JobDetail with the given name - by pausing all of its current Triggers." so i am guessing the trigger is being paused. Also there is no pause method on the trigger itself. Or anything that resembles it. Is simply removing the trigger from the job and the reinserting it my only option to pause a job? I mean this is a pretty trivial thing to want your scheduler to do. How come it doesn't simply work?
  • skaffman
    skaffman over 14 years
    Hmm, good point. I find that with Quartz, trying various things until something works is the most productive approach, since it doesn't always do what it says on the tin.
  • Mohammad Taha
    Mohammad Taha over 14 years
    This is extremely frustrating since there is also no easy way to simply remove a trigger from the job. Can i have a trigger-less job? and if yes, how? Any ideas? I'm starting to lose my patience with this, i have been trying for hours :)
  • Mohammad Taha
    Mohammad Taha over 14 years
    Thank you sooo much :) this worked like a charm. It seems weird that such a simple task as pausing a job would create problems like this.