function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Rajasree JayarajRajasree Jayaraj 

Stop recursive apex batch calls

Hi there,

I have a batch class that checks if a field "Tax ID validation status" is invalid and sends reminder to the Account Owners. My batch basically checks a checkbox and I have a  Worflow rule to trigger email alert when this checkbox is true:

global class InvalidVATReminderBatch implements Database.batchable<sObject>,Schedulable{
 public String query;
    global Database.QueryLocator start(Database.BatchableContext bc){
        return database.getQuerylocator('Select id,Tax_ID_Validation_Status__c,recordtypeId from Account');
    } 
    
   global void execute(Database.batchableContext info, List<Account> accnt){ 
   list<Account>AccList = [Select id,InvalidVATReminder__c,Tax_ID_Validation_Status__c,recordtypeId,recordtype.name from Account where Tax_ID_Validation_Status__c='Invalid'];
   list<Account>saveList = new list<Account>();
   
   if(AccList.size()>0)
   {
   for(Account Acc:AccList)
   {
     if(Acc.Tax_ID_Validation_Status__c=='Invalid')
     {
      Acc.InvalidVATReminder__c=true;
      saveList.add(Acc);
     }
   }
   if(saveList.size()>0)
   {
   try{
     update saveList ;
     }
      catch(Exception e)
     {
      e.getmessage();
     }
   }
  }
 }
  global void finish(Database.batchableContext info)
    {     
    } 
    
    global void execute(SchedulableContext sc) 
    {
      InvalidVATReminderBatch runbatch = new InvalidVATReminderBatch ();  
       ID batchprocessid = Database.executeBatch(runbatch);   
    }
  }

 I have only 20 records that meets this criteria (Invalid status), when I ran the batch it seems to be executing recursively and the same 20 emails are sent out recursively. But the same batch seems to be working well in sandbox, it was executed only once ( received all 20 emails only once). Do you have any idea why the batch was getting executed recursively in Prod?
Here's my batch schedule:
System.schedule('VAT reminder', '0 0 13 4 * ? *', new InvalidVATReminderBatch());

Any help/ ideas would be appreciated.
Thanks
Abhishek_DEOAbhishek_DEO
Not sure how it is working in Sandbox, but you should modify your query to get only those account records where Tax_ID_Validation_Status__c is "invalid" AND "InvalidVATReminder__c" is FALSE. Also query should be executed only once in Start() method(as best practice) and use scope i.e. List<account> in EXECUTE() method.

Thanks

 
Rajasree JayarajRajasree Jayaraj
Thank you Abhishek for the quick respons.
I have a Worflow field update tagged to the same workflow that after the email notification, it unchecks the InvalidVATReminder__c. Also my WF rule condition is "Evaluate the rule when a record is created, and any time it's edited to subsequently meet criteria" and I see the WF field update is working. But since the batch is running recursively, it agains picks up the records and the WF is triggered repeatedly

Could u pls elaborate your point regarding the scope you mentioned in Execute() method. I'm already using global void execute(Database.batchableContext info, List<Account> accnt)

Thanks
 
Abhishek_DEOAbhishek_DEO

My point is you should execute your main query(include your filter conition) in start method() that will collect the record and pass the same records to Execute() method. this way you can avoid extra query in Execute() method. Howeevr, this is not a reason for the issue you are facing.

Are you sure that this query does not return more than 20 records in production? As per your current implementation, batch will pick all records where "Tax_ID_Validation_Status__c" is "Invalid" that is why I ask to include "InvalidVATReminder__c" even you are making this field "InvalidVATReminder__c" is FALSE after email (This would not cause any issue).

One thing you may try.remove below code and use "System.scheduleBatch()" to see if it helps.

global void execute(SchedulableContext sc) 
    {
      InvalidVATReminderBatch runbatch = new InvalidVATReminderBatch ();  
       ID batchprocessid = Database.executeBatch(runbatch);   
    }

Rajasree JayarajRajasree Jayaraj

Hi Abhishek, 

Thank you for your suggestion. Shall make the changes to the query to have it only in one place.

Yes, the query in production returns only 20 records; even I'm receiving the notifications for only those 20 records; but recursively.
I wonder why the batch behaviour is different in Production than sandbox. Seems to run only once in Sandbox; but in production the debug logs and Apex jobs seems to show the batch running every 20-30 seconds.
 

Abhishek_DEOAbhishek_DEO
Okay, check crontrigger entry and asynapexjob table. You might get some more information about the execution and cause.

https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_asyncapexjob.htm
https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_crontrigger.htm
https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_cronjobdetail.htm

From Cronjobdetail table, you will get the ID. Based on this ID, query CronJob and check prev/next fire time and TimesTriggered.