Refire quartz.net trigger after 15 minutes if job fails with exception
Asked Answered
S

4

13

I have searched for an answer on how to retrigger a job after a ceratin amount of time, if the job throws an exception. I cannot see any simple way of doing this.

if I set my trigger up like this:

JobDetail job = new JobDetail("Download catalog", null, typeof(MyJob));
job .Durable = true;
Trigger trigger= TriggerUtils.MakeDailyTrigger(12, 0);
trigger.StartTimeUtc = DateTime.UtcNow;
trigger.Name = "trigger name";
scheduler.ScheduleJob(job , trigger);

And MyJob look like this:

public class MyJob : IJob
{
    public void Execute(JobExecutionContext context)
    {
        var service = new service();


        try
        {
            service.Download();
        }
        catch (Exception)
        {
            throw;
        }

    }
}

how do I make the trigger to refire/retrigger after there is gone 15 minutes if the service.Download() call throws some sort of Exception?

Seaden answered 5/6, 2012 at 7:53 Comment(0)
H
6

I think the only option you have is to trap the error and tell Quartz.net to refire immediately:

public class MyJob : IJob
{
    public void Execute(JobExecutionContext context)
    {
        var service = new service();

        try
        {
            service.Download();
        }
        catch (Exception ex)
        {
              JobExecutionException qe = new JobExecutionException(ex);
              qe.RefireImmediately = true;  // this job will refire immediately
              throw qe;  
        }
    }
}

You can find some info here and here.

UPDATE:

I did some tests and it seems that you can schedule a new trigger inside an executing job.
You can try something like this:

public class MyJob : IJob
{
    public void Execute(JobExecutionContext context)
    {
        var service = new service();

        try
        {
            service.Download();
        }
        catch (Exception ex)
        {
            JobExecutionException qe = new JobExecutionException(ex);
            // qe.RefireImmediately = true;  // this job will refire immediately
            // throw qe;  
            OnErrorScheduleJob(context);

        }
    }

    private void OnErrorScheduleJob(JobExecutionContext context)
    {
        var jobOnError = context.Scheduler.GetJobDetail("ONERRORJOB", "ERROR");
        if (jobOnError == null)
        {
        JobDetail job = new JobDetail("ONERRORJOB", "ERROR", typeof(MyJob));
        job.Durable = false;
        job.Volatile = false;
        job.RequestsRecovery = false;

        SimpleTrigger trigger = new SimpleTrigger("ONERRORTRIGGER",
                        "ERROR",
                        DateTime.UtcNow.AddMinutes(15),
                        null,
                        1,
                        TimeSpan.FromMinutes(100));

        context.Scheduler.ScheduleJob(job, trigger);     
        }
    }
}
Hayse answered 6/6, 2012 at 11:42 Comment(2)
Yeah. This I have read, but it is not a good solution, because it could potential mean that it would refire many thousand times (or even refire million of times). But nice to see that you have come to this conclusion.Seaden
Thanks :D I had sort of the same idea, but this is my first time using quartz so I was not sure if you could use the context.Seaden
E
25

Actually, its not necessary to create a new JobDetail like described by LeftyX. You can just schedule a new trigger that is connected to the JobDetail from the current context.

public void Execute(JobExecutionContext context) {
    try {
        // code
    } catch (Exception ex) {
        SimpleTriggerImpl retryTrigger = new SimpleTriggerImpl(Guid.NewGuid().ToString());      
        retryTrigger.Description = "RetryTrigger";
        retryTrigger.RepeatCount = 0;
        retryTrigger.JobKey = context.JobDetail.Key;   // connect trigger with current job      
        retryTrigger.StartTimeUtc = DateBuilder.NextGivenSecondDate(DateTime.Now, 30);  // Execute after 30 seconds from now
        context.Scheduler.ScheduleJob(retryTrigger);   // schedule the trigger

        JobExecutionException jex = new JobExecutionException(ex, false);
        throw jex;
    }
}

This is less error prone than creating a new JobDetail. Hope that helps.

Effector answered 7/11, 2012 at 13:47 Comment(0)
L
9

I think that the right answer is to use JobListener to retry a job as described here: http://thecodesaysitall.blogspot.cz/2012/03/quartz-candy-part-1.html.

You separate retry logic from Job itself in this solution, so it can be reused.

If you implement retry logic in job as suggested in another replies here, it must be implemented again in every job.

Edit: According to the Ramanpreet Singh note better solution can be found here: https://blog.harveydelaney.com/quartz-job-exception-retrying/

Lascivious answered 26/9, 2013 at 11:10 Comment(3)
I agree. The accepted answer is a "belt and braces" solution (which is fine if you just need to get the job done), but adding a "retry listener" might be a better (and more re-usable) long term solution. I don't think the implementation is perfect, but it's fine place to start... Source on GitHub: github.com/malmgren80/Quartz.CandyPeneus
The linked solution was bettered here: blog.harveydelaney.com/quartz-job-exception-retryingOthilia
@DavidKirkland Your solution rocks. Clean and neatRociorock
H
6

I think the only option you have is to trap the error and tell Quartz.net to refire immediately:

public class MyJob : IJob
{
    public void Execute(JobExecutionContext context)
    {
        var service = new service();

        try
        {
            service.Download();
        }
        catch (Exception ex)
        {
              JobExecutionException qe = new JobExecutionException(ex);
              qe.RefireImmediately = true;  // this job will refire immediately
              throw qe;  
        }
    }
}

You can find some info here and here.

UPDATE:

I did some tests and it seems that you can schedule a new trigger inside an executing job.
You can try something like this:

public class MyJob : IJob
{
    public void Execute(JobExecutionContext context)
    {
        var service = new service();

        try
        {
            service.Download();
        }
        catch (Exception ex)
        {
            JobExecutionException qe = new JobExecutionException(ex);
            // qe.RefireImmediately = true;  // this job will refire immediately
            // throw qe;  
            OnErrorScheduleJob(context);

        }
    }

    private void OnErrorScheduleJob(JobExecutionContext context)
    {
        var jobOnError = context.Scheduler.GetJobDetail("ONERRORJOB", "ERROR");
        if (jobOnError == null)
        {
        JobDetail job = new JobDetail("ONERRORJOB", "ERROR", typeof(MyJob));
        job.Durable = false;
        job.Volatile = false;
        job.RequestsRecovery = false;

        SimpleTrigger trigger = new SimpleTrigger("ONERRORTRIGGER",
                        "ERROR",
                        DateTime.UtcNow.AddMinutes(15),
                        null,
                        1,
                        TimeSpan.FromMinutes(100));

        context.Scheduler.ScheduleJob(job, trigger);     
        }
    }
}
Hayse answered 6/6, 2012 at 11:42 Comment(2)
Yeah. This I have read, but it is not a good solution, because it could potential mean that it would refire many thousand times (or even refire million of times). But nice to see that you have come to this conclusion.Seaden
Thanks :D I had sort of the same idea, but this is my first time using quartz so I was not sure if you could use the context.Seaden
L
0
// don't forget to use @PersistJobDataAfterExecution without it, the jobExecutionContext will reset the value of count.     

SimpleTriggerImpl retryTrigger = new SimpleTriggerImpl();
    retryTrigger.setName("jobname");
    retryTrigger.setRepeatCount(0);
    retryTrigger.setJobKey(jobExecutionContext.getJobDetail().getKey());
    final Calendar cal = getCalendarInstance();
    cal.add(Calendar.MINUTE, 1); //retry after one minute
    retryTrigger.setStartTime(cal.getTime());
    try {
        jobExecutionContext.getScheduler().scheduleJob(retryTrigger);   // schedule the trigger
    } catch (SchedulerException ex) {
        logger.error("something went wrong", ex); 
    }
    JobExecutionException e2 = new JobExecutionException("retrying...");
    e2.refireImmediately();
    throw e2;
Liszt answered 16/1, 2019 at 12:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.