8

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.

11

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)
  • 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 Dec 20 '15 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 Dec 20 '15 at 19:31
11
  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.

  • How would we send the exception_list in an email? Been trying to find examples but having a hard time – user2582622 Mar 20 '18 at 21:54

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.