3

I have written a scheduler class which can schedule a batch job to run every 15 min. I wish to handle a scenario - if there is any exception during scheduling I do not want the chaining of scheduled job to be aborted. The job should keep on getting scheduled even there any error somewhere in the code.

public class abcSchClass implements Schedulable{

   Integer timeInterval;

   public abcSchClass(Integer timeInterval){
       this.timeInterval = timeInterval;
   }

   public void execute(SchedulableContext sc) {  
        try{
        // Re-schedule 
        System.debug('Enter Scheduler class');
        DateTime now  = DateTime.now();
        DateTime nextRunTime = now.addMinutes(timeInterval);
        String cronString = '' + nextRunTime.second() + ' ' + nextRunTime.minute() + ' ' + 
            nextRunTime.hour() + ' ' + nextRunTime.day() + ' ' + 
            nextRunTime.month() + ' ? ' + nextRunTime.year(); 
        System.schedule(abcSchClass.class.getName() + '-' + now.format(), cronString, new abcSchClass(timeInterval));
        // Abort the current job
        Id jobId = sc.getTriggerId();
        System.abortJob(jobId);     
        // Launch the replacement order batch job
        abcbatch b = new abcbatch(); 
        database.executebatch(b);
        system.debug('done scheduling');
        }
        catch(MyExceptionClass e){

        }
   }

}

I just need to run this code from Execute anonymous and it starts scheduling itself every 15 min. How can I handle the exception here? If there is any error in scheduling the current job then the subsequent scheduling should not be aborted permanently. Also I have built my own custom exception to manager exception. Below is my exception class

public class MyExceptionClass extends Exception {
    public MyExceptionClass( Exception e, string sourceClass){   
        throw(e);
    }
}

How can I use this exception class to manage my exception/error scenarios?

2 Answers 2

6

This is realistically impossible. The reason why, of course, is Governor Limits. The way your code is written, if a governor limit hits you, you're dead in the water, indefinitely, until someone creates a new job to start over.

Your best bet would be to simply schedule four jobs that run hourly, 15 minutes apart from each other. While that does seem kind of greedy, it does offer some modicum of reliability, because you're guaranteed to pick up correctly on the next 15 minute window.

That said, if you want maximum reliability, this is one of those times where you probably want to catch Exception (but, make sure you report the errors somewhere!).

The approximate pseudocode for resilient scheduling would look like the following.

public void execute(SchedulableContext context) {
    try {
        executeBatchJob();
    } catch(Exception e) {
        reportException(e);
    }
    try {
        rescheduleIn15Minutes(context);
    } catch(Exception e) {
        reportException(e);
    }
}

Instead of creating a cron job string that only works for the next run, instead schedule it to run with wildcards, like this:

String cronString = '0 '+ nextRun.minute()+' * * * ?';

This means that, even with a governor limit halt, your code will be delayed for an hour instead of dying off never to come back again.

As a further concept, perhaps you could simply create a job that runs hourly, and leverages scheduleBatch instead:

public void execute(SchedulableContext context) {
    try {
        abcbatch batch = new abcbatch();
        Database.executeBatch(batch);
        System.scheduleBatch(batch, 'Now+15', 15);
        System.scheduleBatch(batch, 'Now+30', 30);
        System.scheduleBatch(batch, 'Now+45', 45);
    } catch(Exception e) {
    }
}

Which will allow you to at least kick off the jobs, and the scheduler will simply remain scheduled hourly using the cron string "0 0 * * * ?".

8
  • Thanks. I really appreciate an answer from you as always. I am trying to understand this part- String cronString = '0 '+ nextRun.minute()+' * * * ?'; // "Instead of creating a cron job string that only works for the next run, instead schedule it to run with wildcards.". Why should I use like this instead of mentioning the nextruntime and nexrunhour?
    – SfdcBat
    Dec 28, 2016 at 19:43
  • @SfdcBat Resilience. If you hit a governor limit, your scheduled job will try again in 60 minutes. Without this change, a governor limit exception means that your code will die off and never try again. In fact, as a more proactive measure, you could even query the cronjob object to see if this job has run before--that means that you previously had a governor limit exception, and you could then notify someone preemptively.
    – sfdcfox
    Dec 28, 2016 at 19:46
  • 1
    @SfdcBat That job will have already terminated by then, so there's no need to worry about it. Suicide scheduling was not meant to be "easy", which is why I recommended four jobs, scheduled to be staggered 15 minutes apart. This means that you don't have to worry about aborting/rescheduling, and would drastically reduce the complexity of your code.
    – sfdcfox
    Dec 28, 2016 at 19:51
  • 2
    @SfdcBat you can only throw constructed exceptions of that type if you intend to catch it. I suppose you could catch(Exception e) { throw new CustomException(e, className); } or something like that, but that seems awfully convoluted when you could just write a normal exception logging utility class.
    – sfdcfox
    Dec 28, 2016 at 20:41
  • 1
    @SfdcBat Custom Exceptions are used to throw exceptions that you'd like to catch (or not) that are not system-defined. You might want to ask a separate question on that, because it'll just get lost here in the comments, but basically it allows us to define new exceptions. For example, I once wrote a LimitsCheck class that let me check if I had X resources left, and threw an InsufficentLimitsException otherwise. It's rare to use custom exceptions, but there are times you want to do so.
    – sfdcfox
    Dec 28, 2016 at 21:02
3

I suggest you pause and think about what kind of exception it is that you're trying to "catch". If you encounter a governor limit exception, the scenario you're trying to create won't cover you. You'd need to do a getlimits() first of some kind for the type of limit (see Limits Class) you expect to exceed before you scheduled your job. Unfortunately, there isn't one for Chron Jobs. You'd need to check query limits first, query the chron job object, then also check the flex queue and so forth along with a host of other things. Catching gross "pokeman" exceptions can be ill advised.

4
  • Thank you! Appreciate your inputs as well. I need to think about what exception might occur in this class. If i was to use my own custom exception class would it be possible here?
    – SfdcBat
    Dec 28, 2016 at 19:44
  • If you were to use your own custom error handler class, you'd want to catch errors, like DML exceptions and report them via some kind of logic, either via writing back to a custom object, through an email handler or writing to a custom field on the existing object. It really depends on the type of error you're getting as to how you apply the logic. Some errors can be repaired if you have a less generic handler than what your using.
    – crmprogdev
    Dec 28, 2016 at 20:14
  • I am planning to just using debug.Log for now. Can you let me know how can I modify the MyExceptionClass to catch one kind of exception. Is it possible to show an example here? I am just going to schedule a batch job 3 times and run a batch job here.
    – SfdcBat
    Dec 28, 2016 at 20:20
  • 3
    I suggest you look at Creating Custom Exceptions in the Apex Documentation. There's ample examples to help you figure out how to do what you want. It's not entirely clear to me what functionality you're looking for in creating a custom Exception Class to catch an existing type of exception, but I think the link will give you what you need to accomplish it.
    – crmprogdev
    Dec 28, 2016 at 20:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .