javaquartz-schedulerquartz

Quartz retry job with exponentially increasing delay time strategy for trigger


I use quartz in spring project. The idea is to create separate job for new income data, that was not delivered successfully to target service.

Both ways can provide correct flow, but I prefer out the box solution.

I tried to manage JobExecutionContext of triggered job to use same JobDetail registering it in quartzScheduler. The idea was to update existing job with different trigger. But issue is quartz tries to create new job persisting it in DB.

org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'digex-caas-securepay.b333e5bf-583f-4643-9ad7-ef4b913001f7', because one already exists with this identification.
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1113) ~[quartz-2.3.0.jar:na]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$2.executeVoid(JobStoreSupport.java:1067) ~[quartz-2.3.0.jar:na]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$VoidTransactionCallback.execute(JobStoreSupport.java:3765) ~[quartz-2.3.0.jar:na]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$VoidTransactionCallback.execute(JobStoreSupport.java:3763) ~[quartz-2.3.0.jar:na]
    at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:245) ~[quartz-2.3.0.jar:na]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJobAndTrigger(JobStoreSupport.java:1063) ~[quartz-2.3.0.jar:na]
    at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:855) ~[quartz-2.3.0.jar:na]
    at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:249) ~[quartz-2.3.0.jar:na]
    at com.incomm.ecomm.services.quartz.OrderQuartzJobScheduler.registerSecurePayPostServiceJob(OrderQuartzJobScheduler.java:59) ~[classes/:na]

Questions are (please answer any):


Solution

  • Simple answer is:

    scheduler.rescheduleJob(trigger.getKey(), trigger);
    

    Detailed answer is:

    • How to manage via quartz job trigger updates

    scheduler.rescheduleJob(trigger.getKey(), trigger);

    • How to manage via quartz job updates

    It is not important any more in case trigger updated.

    • How to register exponentially increasing delay time strategy for trigger?

    single trigger can be rescheduled with any different time. Time for next execution can be calculated using any implementation of IntervalCalculationStrategy.

    Example of reschedule the job:

    The job and job details can be taken from JobExecutionContext but not necessary. Trigger can be connected to only one job, so it good enough for quartz specify triggerKey to be updated:

    @Autowired
    private Scheduler scheduler;
    @Autowired
    private IntervalCalculationStrategy intervalCalculation;
    
    public <T extends QuartzJobBean> void registerSecurePayPostServiceJob(
        JobExecutionContext firedJobExecutionContext) {
      Optional<SimpleTriggerImpl> mutableTrigger =
          ofNullable(firedJobExecutionContext)
              .map(JobExecutionContext::getTrigger)
              .filter(SimpleTriggerImpl.class::isInstance)
              .map(SimpleTriggerImpl.class::cast);
      try {
        if (mutableTrigger.isPresent()) {
          SimpleTriggerImpl trigger = mutableTrigger.get();
          int nextAttemptNumber = trigger.getTimesTriggered();
          log.trace("trigger: {} fired [{}] times", trigger.getFullName(),
              trigger.getTimesTriggered());
          trigger.setStartTime(intervalCalculation.calculateNextTryDate(nextAttemptNumber));
          this.scheduler.rescheduleJob(trigger.getKey(), trigger);
        }
      } catch (SchedulerException e) {
        log.error("job was not rescheduled <{}>", firedJobExecutionContext.getJobDetail(), e);
      }
    }