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?

  • What if you try to catch System.AsyncException in the first catch? It might be a "special" sf type of exception otherwise... – Boris Bachovski Apr 15 '13 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 '13 at 1:57
  • Nah, same deal as expected. – Matt Lacey Apr 15 '13 at 1:58
  • No need to count invocations, you can query the number of pending and in progress jobs directly – Ralph Callaway Apr 15 '13 at 8:20
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.

|improve this answer|||||
  • success.salesforce.com/ideaView?id=08730000000kmjRAAQ idea worth upvoting ;) – pjcarly Apr 15 '13 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 '13 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 '13 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());
}
|improve this answer|||||
  • Exactly what I ended up doing earlier today! – Matt Lacey Apr 15 '13 at 9:53
  • Must have been that stackechange mind meld happening ;) – Ralph Callaway Apr 15 '13 at 17:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.