Saturday, 4 July 2015

How to control two batch job execution ?

We are all familiar with batch class in SFDC. This  post is all about to discuss a known issue in batch class and what is workaround for that?

As we know batch class is is required when we are dealing with large number of records and total number of batch job will be decided based on scope parameter (defined while calling a batch class) and total number of records.
Say total number of records = 100
Scope parameter = 5.  Database.executeBatch(new batchclass(),5);
Then batch job will be 20 which means each job will process 5 records.

We can see how many jobs are pending and how many jobs completed in set up-->apex jobs.

While one batch is in progress we invoke same batch class another time having batch size 5 and total number of records 50. Ideally number jobs should be 10 but actually it will be 10 + number of progressing job of previous batch. Which causes data redundancy that means same records will be processed two times in both the batch.This is the known issue in batch class.

The prime motto is two batch should not pic same records.

Let me share the issue what i had come across. External system were passing all the data to SFDC (SFDC had exposed a service to receive data from) then i was storing in staging table(temporary obj) then i was calling batch class after data is inserted in staging table to push into original object.I was calling batch class from a trigger.let say batch size is 5 and 20 records inserted in staging obj then obviously batch will be invoked and 4 jobs will be progress. While third batch job is running another 30 set of records inserted in staging obj then again batch will be invoked and total number jobs will be 30/5=6+ two jobs of previous batch  = 8 jobs will be in progress.That means 2jobs= 10 records will be processed two times.In our original object 50 records should be created but actually total number if records will be 55. 5 duplicate records which is an issue.

Solution :-

  •  Create a flag(check box field) in staging obj. When original records will be created set that flag is true so that after one week these records will be deleted from staging obj.
  • Create a class where check whether batch is in progress or not if not call batch class
  • Scheduler same class every one minute interval until all the progress batch job is completed and check if there is few records to be processed in staging obj.
  • Call same class from trigger if same class is not scheduled.
Technical Details:-
String batchClassId = '01pS0000000Fm2W';
String  schedulerclassId = '01pS0000000FvKF';

global class Scheduling_Svc_WS_CreateBatch implements Schedulable{    // Execute method     global void execute(SchedulableContext SC) {     // Code to be executed when the schedule class wakes up           // code           list<BRASS_Case__c > listOfBrassCases;           list<AsyncApexJob> jobList;           try{                jobList = [select id,ApexClassID,JobType,Status from AsyncApexJob where ApexClassID =:batchClassId AND Status ='Processing'];                listOfBrassCases  = [select id from BRASS_Case__c where Is_Org_Case_Created__c !=true];                          if(jobList.size()==0){             //String cronID = System.scheduleBatch(new Svc_WS_CreateCaseBatch(), 'original case creation', 5);             Database.executebatch(new Svc_WS_CreateCaseBatch(),5);                        }           }catch(QueryException ex){             system.debug('pringint exception---'+ex);           }            // This section of code will schedule the next execution 1 minutes from now     if(listOfBrassCases.size()>0 && (!(jobList.size()>0) || jobList.size()>0)){       datetime nextScheduleTime = system.now().addMinutes(1);         string minute = string.valueof(nextScheduleTime.minute());         string second = string.valueof(nextScheduleTime.second ());         string cronvalue = second+' '+minute+' 0-23 * * ?' ;         string jobName = 'selfReschedulingClass ' +nextScheduleTime.format('hh:mm');           Scheduling_Svc_WS_CreateBatch p = new Scheduling_Svc_WS_CreateBatch();       system.schedule(jobName, cronvalue , p);       system.abortJob(sc.getTriggerId());               }     else{       // this section of code will abort the current schedule job       system.abortJob(sc.getTriggerId());       }              }  }

From Trigger you call above class like this
try{
                list<AsyncApexJob> jobList = [select id,ApexClassID,JobType,Status from AsyncApexJob where ApexClassID =cAND Status ='Queued'];
                if(!(jobList !=null && jobList.size()>0)){
                    datetime nextScheduleTime = system.now().addMinutes(1);
                    string minute = string.valueof(nextScheduleTime.minute());
                    string second = string.valueof(nextScheduleTime.second ());
                    string cronvalue = second+' '+minute+' 0-23 * * ?' ;
                    string jobName = 'selfReschedulingClass ' +nextScheduleTime.format('hh:mm');
                 
                    Scheduling_Svc_WS_CreateBatch p = new Scheduling_Svc_WS_CreateBatch();
                    system.schedule(jobName, cronvalue , p);
                }
                
     
           }catch(Exception ex){
           }


No comments: