[SalesForce] Batch apex failling silently for some records

I am writing a batchable apex class and am finding that every time I run it, some rows are not being processed.

The strange thing is when I look at the apex job monitoring page, the number of batches is correct when the job starts running, but the number of batches reduce right at the end by about the right number to explain the records that haven't been updated.

The number of records that are missed remains the same when I run the batch process multiple times.

When I single out a record that was missed to be updated by providing its id to the queryLocator, it updates fine.

I looked at the Limits but they all look fine. I created a really simple batchable process that is not doing anything fancy, it is just blanking out some fields to try and understand the problem. It's not doing any queries or loading any extra data.

I put some logging in and found that although I'd requested the batch size to be 30, there were lots of batches where only 10 records were processed. There seemed to be reliably either 30 or 10 records in the batch.

Here's my Batchable apex class:

global class NBTypeBatchUpdate implements Database.Batchable<sObject> {
global Database.QueryLocator start(Database.BatchableContext BC)
{
    String query = 'SELECT Id, Name, NBType__c ';
    query += 'FROM Opportunity ';

    return Database.getQueryLocator(query);
}

global void execute(Database.BatchableContext BC, List<sObject> scope)
{
    system.debug('Processing ' + scope.size() + ' opportunities');

    for (Opportunity opportunity : (List<Opportunity>)scope){
        system.debug('Processing ' + opportunity.Name);
        opportunity.NBType__c = null;
    }

    update scope;
}  

private static void PrintLimits() {
    system.debug('Queries: ' + Limits.getQueries() + '/' + Limits.getLimitQueries());
    system.debug('Query locator rows: ' + Limits.getQueryLocatorRows() + '/' + Limits.getLimitQueryLocatorRows());
    system.debug('CPU time: ' + Limits.getCpuTime() + '/' + Limits.getLimitCpuTime());
    system.debug('Heap size: ' + Limits.getHeapSize() + '/' + Limits.getLimitHeapSize());
    system.debug('Heap size: ' + Limits.getHeapSize() + '/' + Limits.getLimitHeapSize());
}

global void finish(Database.BatchableContext BC)
{
}

}

Best Answer

Turned out that something had gone wrong with some of the records when creating the sandbox. It was a full sandbox with a copy of all the data from production.

In my case, I was able to reproduce the issue by creating a new column on Opportunity and a batch process that did nothing except update that column. I could see that some rows weren't being updated. Support were able to reproduce the issue in my sandbox as well, so they were able to diagnose and solve it.

Here was the cut down batch process I used to demonstrate the problem:

global class NBTypeBatchUpdate implements Database.Batchable<sObject> {
    global Database.QueryLocator start(Database.BatchableContext BC)
    {

        String query = 'SELECT Id, WasUpdatedByBatchUpdate__c ';
        query += 'FROM Opportunity ';

        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, List<sObject> scope)
    {
        system.debug('Processing ' + scope.size() + ' opportunities');

        for (Opportunity opportunity : (List<Opportunity>)scope){
            opportunity.WasUpdatedByBatchUpdate__c = true;
        }

        update scope;
    }  

    global void finish(Database.BatchableContext BC)
    {
    }
}

I didn't get any error messages from the update dml statement. The trouble records just didn't get updated.

Here's exactly what support told me (in case anyone else has this problem):

it seems this was an unfortunate consequence of the non blocking copy process used when refreshing a sandbox which left some data in an inconsistent state.

We ran scrutiny to fix the issue. It seems the issue is resolve for now.

Related Topic