7

I have a simple requirement to write a batch which will loop through all the records for 'Object__c' and update a custom field on the object(Logic is irrelevant here).

Here is the batch class I developed:

public class SimpleBatch implements Database.Batchable<sObject>{

    public Database.QueryLocator start(Database.BatchableContext BC){
        return Database.getQueryLocator('SELECT Id,Name,Start_Date__c,End_Date__c,Active__c FROM 
                        Object__c WHERE Start_Date__c!=NULL AND End_Date__c!=NULL');
    }

    public void execute(Database.BatchableContext BC,List<Package__c> scope){
        Date todayDate = Date.today();
        List<Object__c> objsToUpdate = new List<Object__c>();
        for(Object__c obj:scope){
            if((obj.Start_Date__c <= todayDate) && (obj.End_Date__c >=todayDate)){
                if(pkgObj.Active__c != 'Yes'){
                    pkgObj.Active__c = 'Yes';
                    objsToUpdate.add(obj);
                }
            }else if((obj.Start_Date__c > todayDate) || (obj.End_Date__c < todayDate)){
                if(obj.Active__c != 'No'){
                    obj.Active__c = 'No';
                    objsToUpdate.add(obj);
                }
            }
        }
        update objsToUpdate;
    }

    public void finish(Database.BatchableContext BC){
        //code to send email that batch finished successfully.
    }

}

Lets assume there are 1000 records to process and I am running the job with a batch size of 200 records.

  1. Batch 1 - First set of 200 records - Records failed with validation errors.
  2. Batch 2 - Second set of 200 records - Records failed with validation errors.
  3. Batch 3 - Third set of 200 records - Records failed with validation errors.
  4. Batch 4 - Fourth set of 200 records - Records failed with validation errors.
  5. Batch 5 - Fifth set of 200 records - successfully processed, No errors.

Here is the problem: Even though first batches failed, The email is sent from the finish method saying that batch successfully processed.

Only When I look into the logs, I will know that there are some records failed otherwise everything looks to seem fine even though 4 batches failed.

Any best practices or advice on how to handle this situation.

2 Answers 2

12

Some things to do:

  1. Have your batch class implement Database.stateful
  2. Declare some variables that are initialized in the constructor to 0. These variables count successes and errors; also a string variable that remembers the records that failed and why (initialized to empty string).
  3. Use Database.update with allOrNothing = false instead of update within execute(). Interrogate each member of Database.SaveResult[] for isSuccess() and count succcesses and failures in your stateful variables from #2. Log in the stateful variable all the errors (id of record, name of record, and error message/exception)
  4. In the finish method, send an email to the sysad of count of successes/failures + string variable of all the failures.
  5. In finish() method, write your batch results to a custom Log__c record(s).dneklnd
2
  • 3
    Decent answer, but two additions: query the job Id in the finish method, to check for blown governor limits, and use a try catch block in the execute method to catch whatever unusual exceptions may occur that one can catch, so they can be recorded properly.
    – sfdcfox
    Commented Dec 20, 2015 at 18:42
  • try-catch - yes, of course; as for querying the ApexAsyncJob in finish() - I never thought of that before to catch limits exceptions - so thanks! Also, I'd probably redo the logging approach if batch was huge and incidence of errors was high to avoid blowing heap
    – cropredy
    Commented Dec 20, 2015 at 19:31
13
  1. first of all you need to make your batch class stateful using Database.Stateful so replace your first line with

    public class SimpleBatch implements Database.Batchable<sObject>,Database.Stateful{
    
  2. A global variable required which will maintain failed record.

    global List<String> exception_List;
    
  3. Use Database.update method instead of update with allOrNothing = false parameter which will return how many records has passed and failed in update call.

    Database.SaveResult[] SaveResultList = Database.update(objsToUpdate,false);   
    
  4. You will iterate saveResultList and you will add failed records in exception_list

     for(integer i =0; i<objsToUpdate.size();i++){
            String msg='';
            If(!SaveResultList[i].isSuccess()){
                    msg += userList.get(i).id + '\n'+'Error: "';        
                    for(Database.Error err: SaveResultList[i].getErrors()){  
                         msg += err.getmessage()+'"\n\n';
                    } 
            }
            if(msg!='')
                exception_List.add(msg);
         } 
    
  5. You can use this exception_list in execute method to send in your final email.

2

You must log in to answer this question.

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