Mário Meyrelles
Mário Meyrelles

Reputation: 1792

Handling failures of chained jobs using Quartz.NET

I was able to schedule 3 chained jobs using Quartz.NET. This strategy is working fine:

var j1 = new TestJob1();
var j2 = new TestJob2();
var j3 = new TestJob3();

var jd1 = j1.Build();
var jd2 = j2.Build();
var jd3 = j3.Build();


var chain = new JobChainingJobListener("jobchain");
chain.AddJobChainLink(jd1.Key, jd2.Key);
chain.AddJobChainLink(jd2.Key, jd3.Key);


Scheduler.ListenerManager.AddJobListener(chain, GroupMatcher<JobKey>.AnyGroup());

Scheduler.ScheduleJob(jd1, j1.JobTrigger());
Scheduler.AddJob(jd2, true);
Scheduler.AddJob(jd3, true);


Scheduler.Start();

The code for each job is as follows:

public class TestJob1 : BaseJob, IJob
    {
        public override ITrigger JobTrigger()
        {
             return TriggerBuilder.Create()
                 .WithSimpleSchedule(
                     ssb =>
                     ssb.WithInterval(new TimeSpan(0, 0, 0, 10)).RepeatForever().WithMisfireHandlingInstructionFireNow())
                 .Build();
        }

        public void Execute(IJobExecutionContext context)
        {
            Debug.WriteLine($"Running Job 1 at {DateTime.Now.ToString("O")}");
        }
    }

    public class TestJob2 : BaseJob, IJob
    {
        public override ITrigger JobTrigger()
        {
            throw new System.NotImplementedException();
        }

        public void Execute(IJobExecutionContext context)
        {
            Debug.WriteLine($"Running Job 2 at {DateTime.Now.ToString("O")}");
            throw new Exception("forced error");
        }
    }

    public class TestJob3 : BaseJob, IJob
    {
        public override ITrigger JobTrigger()
        {
            throw new System.NotImplementedException();
        }

        public void Execute(IJobExecutionContext context)
        {
            Debug.WriteLine($"Running Job 3 at {DateTime.Now.ToString("O")}");
        }
    }

If you see, the TestJob2 is throwing an exception when it runs. Even on this situation, the TestJob3 is fired. My business requirement is that TestJob3 shouldn't be fired if TestJob2 fails. Notice that actually I don't need to implement the trigger for job2 and job3 because I'm adding those jobs without a trigger to the scheduler.

How would this be done?

Thanks in advance, Mário

Upvotes: 4

Views: 1221

Answers (3)

transistor1
transistor1

Reputation: 2925

Subclass JobChainingJobListener, and use JobChainingJobListenerFailOnError in place of JobChainingJobListener:

/// <summary>
/// JobChainingJobListener that doesn't run subsequent jobs when one fails.
/// </summary>
public class JobChainingJobListenerFailOnError : JobChainingJobListener
{
    public JobChainingJobListenerFailOnError(String name) : base(name) { }

    public override void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException)
    {
        //Only call the base method if jobException is null.  Otherwise, an
        //error has occurred & we don't want to continue chaining
        if (jobException == null)
            base.JobWasExecuted(context, jobException);
    }
}

Upvotes: 2

Alioza
Alioza

Reputation: 1770

I've had to chain jobs in the past but I did not use JobChainingJobListener. What I do is add and schedule the (n+1)th job when the (n)th job is done. This is helpful for example when the next job to execute depends on the result of the current job, as it does in your case.

To continue to use JobChainingJobListener I think you could get the TestJob3 and set a flag in its data map, when TestJob2 ends successfully. TestJob3 will still be executed when there is an exception in TestJob2 but you just have to check your flag to see if it needs to carry on with its execution.

Upvotes: 1

mg3
mg3

Reputation: 131

While the JobChain is new to me I would still employ base clr api calls and take advantage of the Task Parallel library (tpl) by chaining tasks (https://msdn.microsoft.com/en-us/library/ee372288(v=vs.110).aspx).

This particular code chains four different tasks together and one cannot fire without the previous finishing. All i want Quartz to do is schedule and call my job. I apologize if i veered away from the quartz api but i just wanted provide a way i handled multiple tasks.

In my job i enter the job Execute and execute calls Process()

private async Task<Boolean> Process()
{   
   Task<bool> t1 = Task<bool>.Factory.StartNew(() =>
   {
      return processThis();
   });


   Task<bool> t2 = t1.ContinueWith((ProcessMore) =>
   {
      return processMoreStuff();
   });

   Task<bool> t3 = t2.ContinueWith((ProcessEvenMore) =>
   {
      return processEvenMoreStuff();
   });

   Task<bool> t4 = t3.ContinueWith((ProcessStillSomeMore) =>
   {
      return processStillMoreStuff();
   });


    var result = await t4;

   try
   {
      Task.WaitAll(t1, t2, t3, t4);
   }
   catch (Exception ex)
   {
      System.Diagnostics.Trace.WriteLine(ex.Message);
   }

   return result;
}

Upvotes: 1

Related Questions