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
    Jul 16 '18 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 Jul 16 '18 at 19:05
  • I am still having the same issue. Is there any other missing status? Jul 16 '18 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
    Jul 17 '18 at 2:21
  • I am using the execute anonymous. Jul 17 '18 at 7:35

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

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.