1

I have written a retry mechanism for batch which triggers when the batch records fails with UnableToLockRow exception, we will call this batch from the finish methods with the failed records.

I need to retry "RetryBatchApex" batch only three times per transaction. for eg, if an exception from batch triggers this "RetryBatchApex" batch, then this batch "RetryBatchApex" should run only three times. after which we will save the records to exception log.

I dont know how to implement the retry logic, I thought of using a static counter, but that will reset as soon as the batch "RetryBatchApex" is again triggered from the finish method of "RetryBatchApex".

this is my batch class, I have commented the line where my retry check should go. Any ideas?

global class RetryBatchApex implements Database.Batchable<sObject>, Database.Stateful {

    private List<SObject> failedSObjects;

    global RetryBatchApex(List<SObject> failedSObjects) {
        this.failedSObjects = failedSObjects;
    }

    global Iterable<sObject> start(Database.BatchableContext param1) {
        return failedSObjects;
    }

    global void execute(Database.BatchableContext param1, List<Object> param2) {

    }

    global void finish(Database.BatchableContext param1) {

        List<SObject> newFailedObjects = new List<SObject>();
        if (failedSObjects != null && !failedSObjects.isEmpty()) {
            if (failedSObjects.size() + Limits.getDMLRows() < Limits.getLimitDMLRows()) {
                List<Database.upsertResult> resultList = Database.upsert(failedSObjects, false);
                List<Exception_Log__c> exceptionList = new List<Exception_Log__c>();
                for (Integer i = 0; i < failedSObjects.size(); i++) {
                    Database.upsertResult sr = resultList[i];
                    if (!sr.success) {
                        for (Database.Error err : sr.getErrors()) {
                            // add the ids sObject which failed again.
                            // We can try retrying upto 3 times.
                            if (err.getStatusCode() == StatusCode.UNABLE_TO_LOCK_ROW) {
                                newFailedObjects.add(failedSObjects[i]);
                            }
                        }
                    }
                }
            }
        }
        // implement retry logic here so that the RetryBatchApex runs only three times in a transaction and not more that that
        if (!newFailedObjects.isEmpty()) {
            Database.executeBatch(new RetryBatchApex(newFailedObjects));
        }
    }


}

1 Answer 1

3

You're right that a static variable won't work, as your batches are all going to execute in separate contexts. What you can do (since you already implement Database.Stateful) is store the current retry count as an instance variable, check it before you spawn a new batch, and then increment and pass the count to the newly-spawned batch:

private Integer retryCount;

global RetryBatchApex(List<SObject> failedSObjects) {
    this.failedSObjects = failedSObjects;
    this.retryCount = 0;
}

global RetryBatchApex(List<SObject> failedSObjects, Integer retries) {
    this.failedSObjects = failedSObjects;
    this.retryCount = retries;
}

Then, in finish(), you check whether or not to spawn a new batch, and give it the existing retry count + 1:

    if (!newFailedObjects.isEmpty() && retryCount < 3) {
        Database.executeBatch(new RetryBatchApex(newFailedObjects, retryCount++));
    }
2
  • Ok so as I understood, I also need to change the constructor to global RetryBatchApex(List<SObject> failedSObjects, Integer retryCount) Mar 19, 2018 at 14:09
  • I am suggesting you add a new constructor (which can of course call through to the existing one), but yes, you could just change it and then call it with a retryCount of 0 when you first start the batch.
    – David Reed
    Mar 19, 2018 at 14:10

You must log in to answer this question.

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