I need a batch that can be executed at most once. I came out with the following code:

global class ProcessABatch implements Database.batchable<SObject>, Database.Stateful {

    global Database.Querylocator start(Database.BatchableContext bc) {

    global void execute(Database.BatchableContext bc, List<SObject> sObjectList) {

    global void finish(Database.BatchableContext bc) {

    public static Id startBatch(Date invoiceDate) {
        ProcessABatch pab = new ProcessABatch();
        Integer batchSize = 1;
        Id jobId = null;

        if (!IS_BatchUtils.isBatchExecuted(pab)) {
            jobId = Database.executeBatch(pab, batchSize);

        return jobId;

public class IS_BatchUtils {
    static public String getClassName(Object obj) {
        return String.valueOf(obj).substring(0, String.valueOf(obj).indexOf(':'));

    static public Boolean isBatchExecuted(Object obj) {
        Boolean isBatchExecuted = false;
        String className = IS_BatchUtils.getClassName(obj);

        if ([SELECT count() FROM AsyncApexJob WHERE JobType = 'BatchApex' AND Status IN ('Processing', 'Preparing', 'Queued', 'Holding') AND ApexClassId IN (SELECT Id FROM ApexClass WHERE Name = :className)] > 0) {
            isBatchExecuted = true;

        return isBatchExecuted;

This is how I execute the batch(execute anonymous):

IS_CreateInvoicesPerAccountBatch.startBatch(Date.today()); IS_CreateInvoicesPerAccountBatch.startBatch(Date.today());

For some reason the batch can be executed more than once. Am I doing something? Can you provide a working version?

  • 2
    You may have forgotten about Holding status.
    – sfdcfox
    Commented Jul 16, 2018 at 19:01
  • I see, I was looking at the following salesforce article, they did not mentioned the holding status: help.salesforce.com/articleView?id=code_apex_job.htm&type=5 Commented Jul 16, 2018 at 19:05
  • I am still having the same issue. Is there any other missing status? Commented Jul 16, 2018 at 20:16
  • the two lines in yellow - are these lines from exec anonymous? add a soql query after the first line to display all the rows/values in AsyncApexJob
    – cropredy
    Commented Jul 17, 2018 at 2:21
  • I am using the execute anonymous. Commented Jul 17, 2018 at 7:35

1 Answer 1


I think the issue is that the batch jobs start in the "post-commit logic" of a transaction. So, they don't get added to the AsyncApexJob table until after the end of your transaction. So, when you call IS_CreateInvoicesPerAccountBatch.startBatch(Date.today()); twice in a row, there's no change to the AsyncApexJob until after both of those calls have completed.

A better way is to go via a custom object to coordinate your batches. Like this: Guarantee only a single asynchronous job runs at a time

You must log in to answer this question.

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