11

I've got a Visualforce page that allows the user to fire a batch job manually, using this action method:

public void FireBatch()
{
    try
    {
        Database.ExecuteBatch(new AutobotBatch());
    }
    catch (System.LimitException e)
    {
        ApexPages.AddMessage(new ApexPages.Message(ApexPages.Severity.Info, 'There are too many jobs queued to run.'));
    }
    catch (Exception e)
    {
        ApexPages.AddMessage(new ApexPages.Message(ApexPages.Severity.Error, 'Oh snap: ' + e.GetMessage()));
    }
}

Yet despite the try and catch blocks, if I play the part of an over-keen user and keep hitting the commandButton over and over I get the usual error:

"Attempted to schedule too many concurrent batch jobs"

I know what this means, and I know I could probably count invocations myself; but what I want to do know is why I can't seem to catch this exception. Perhaps the exception is fired in the context of the asynchronous job, but in that case it shouldn't be redirecting the user's browser to the error page.

Anybody know how to catch this and simply display a message without resorting to tracking the jobs manually?

4
  • What if you try to catch System.AsyncException in the first catch? It might be a "special" sf type of exception otherwise... Apr 15, 2013 at 1:18
  • The exception shown in the error is LimitException, hence I added that check. Otherwise the other part should catch all. I'll give it a crack though out of curiosity.
    – Matt Lacey
    Apr 15, 2013 at 1:57
  • Nah, same deal as expected.
    – Matt Lacey
    Apr 15, 2013 at 1:58
  • No need to count invocations, you can query the number of pending and in progress jobs directly Apr 15, 2013 at 8:20

2 Answers 2

11

You can't catch LimitException. They are a special class of fatal error that simply cause your code to blow up the minute you hit one.

So your only strategy is going to be avoidance. Counting invocations of this code is one way, but that will only work if this is the only place batch jobs can be scheduled. Keeping a system-wide counter (e.g. batch jobs scheduled in last N minutes) in a custom setting would be another way but that's also flawed.

This is one of those silly limits that seems hard to justify - why limit the number of queued batch jobs, especially to an outrageously small number like 5? If SFDC just managed the queue properly, the cost of queuing a job should be negligible. But I digress.

If you think this is going to be a real problem for normal usage, the only solution I can offer is one that we came up with to avoid a slightly different limit - "Total number of classes that can be scheduled concurrently". But the solution is the same.

  • Create a custom object, Queued_Job__c, that holds a class name, an executed flag, and maybe a log field and/or some optional parameters. Instead of calling executeBatch directly, you save a new instance of this object.
  • Have a background worker job, as a scheduled Apex job, whose job is to wake up, check the table, and if anything exists in an unexecuted state it executes the earliest requested job as an executeBatch call.
  • At the end of the worker job, it reschedules itself to execute again in a few minutes.

This still has the issue that if not all code in the org is using this approach, you can still run into issues of course, but it will scale to a near-unlimited number of concurrently queued jobs.

3
  • success.salesforce.com/ideaView?id=08730000000kmjRAAQ idea worth upvoting ;)
    – pjcarly
    Apr 15, 2013 at 8:09
  • Tell that to the ideas that are 5+ years old, have 20k+ points, and are still not on the roadmap :(
    – jkraybill
    Apr 15, 2013 at 12:29
  • one can only cross the fingers and hope for a better salesforce platform (instead of seeing 200 new functions each new release cycle, that 2 men and their dog will use, instead of updating the core platform a bit more, See True To the Core (TTTC) webinars/dreamforce sessions
    – pjcarly
    Apr 15, 2013 at 15:54
10

While you can't catch a LimitException (which seems strange since it let's you define a catch block for it), you can query how many batch jobs are running and use that to determine if you have the resources available to submit a batch job. Unfortunately there isn't a limits method for batches so you'll have to hard code it for now.

Example

Integer MAX_BATCHES = 5;
Integer runningBatchJobs = [
  select count()
  from AsyncApexJob
  where JobType = 'BatchApex'
  and status in ('Queued','Processing','Preparing')
];

if(runningBatchJobs >= MAX_BATCHES) {
  ApexPages.addMessage(new ApexPages.Message(
    ApexPages.Severity.WARNING, 'No resources available.  Please try again later.'
  ));
} else {
  Database.executeBatch(new AutobotBatch());
}
2
  • Exactly what I ended up doing earlier today!
    – Matt Lacey
    Apr 15, 2013 at 9:53
  • Must have been that stackechange mind meld happening ;) Apr 15, 2013 at 17:57

You must log in to answer this question.

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