1

I'm making a call out to get an auth token in the start() method of my batch class (I'm implementing the stateful interface and using a class variable to save this token for the entire batch transaction). Here's a simplified version of my start method.

public Database.QueryLocator start(Database.BatchableContext bc)
{
    // Retry for token
    Boolean receivedToken = false;
    Integer retryCount = 0;
    do
    {
        receivedToken = Helper.getAuthToken();
        retryCount++;
    }
    while (!receivedToken && retryCount <= 3);

    if(receivedToken)
    {
        this.AUTH_TOKEN = Helper.AUTH_TOKEN;
    }
    else
    {
        // Returning an empty result
        return Database.getQueryLocator('SELECT Id FROM Custom_Object__c WHERE CreatedDate = TOMORROW');
    }

    return Database.getQueryLocator(this.QUERY);
}

My intention is to avoid the execution of the execute() method when the auth token request fails, but still allow the finish() method to process all of its logic. As you see I'm making a query that I know will return an empty result in all situations and this is working fine, But is this proper the way to do this? I've also posted this comment here which might help someone with a slightly different situation.

I appreciate any suggestions! 🍻

2
  • 2
    Returning a query that is guaranteed empty is the only way to do this that I am aware of. Whilst what you return should never return anything, what I use is a WHERE Id = NULL. This is likely not massively efficient, of course, but we are async here so that is less of a concern. I would ask why you don't get the token in the constructor and decide to Database.executeBatch or not based on calling a method on the batch instance that itself uses whether or not a token was obtained...? This could be embedded in a method on the batch itself, like myBatch.executeIfConnected()...
    – Phil W
    Dec 31 '20 at 19:40
  • My main concern is that the finish method of my batch class has logic that needs to run such as aborting any exiting same job and re-scheduling for the next run.
    – Bahman.A
    Dec 31 '20 at 19:47
2

You could actually just:

public Iterable<Custom_Object__c> start(Database.BatchableContext bc)

And if you don't want any execution to occur:

return new Custom_Object__c[0];

This returns an empty list, so no execute call will happen, and you don't need a query. Note that you'll have to cast the QueryLocator to use this technique:

return (Iterable<Custom_Object__c>)Database.getQueryLocator(this.QUERY);
4
  • 2
    Is it safe to return an iterator if you want to actually return a query locator (to handle more than 50000 records) in the other flow, where there is data to process?
    – Phil W
    Dec 31 '20 at 20:30
  • @PhilW Great point thanks for bringing this up. Hi sfdxfox, I'm also interested to know if this way of handling this has any advantages over the way I'm currently doing this. Thanks so much!!
    – Bahman.A
    Dec 31 '20 at 20:34
  • 1
    @PhilW Yes, I actually use this in my Swiss Army Knife Batch Process, which we use a similar form of production to mass update millions of records.
    – sfdcfox
    Dec 31 '20 at 21:01
  • Cool. I will look at changing how we deal with this if that dynamic switching between iterator and query locator works like you suggest!
    – Phil W
    Dec 31 '20 at 21:09

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.