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
Sierra VPSierra VP 

Limits on Batch Apex with Trigger: Will it be 5 or 105?

I'm new to developing and trying to determine if I will get errors thrown in my batch apex code. The purpose is to: upon deactivation of a user (trigger, which I understand isn't best practice but don't know another way the batch can be automatically started w/o checking for deactivated users every night), all of the leads of deactivated user are queried to change the OwnerId to a catchall user. The trick is that one user may have as many as 200,000 leads that need to be updated, hence why I am using batch apex. Two questions:

1. How many batch jobs will be activated with each deactivated user? (I think the answer is one?)

2. Will the batch job be sent to the Flex Queue as 'holding' and then to active, giving the org 105 possible batch jobs to be held/processed at once before throwing errors, or will it bypass Flex Queue, thereby limiting the org to only 5 batch jobs at a time?

I hope my questions make sense, and I appreciate any help. I have spent many hours researching this as a newbie. In case it's relevant, my code is below:

Trigger:

trigger BatchUpdateTrigger2 on User (after update)  {

//Map will keep deactivated users and their Id's, to be used later in batch Apex
	 Map<id, User> DeactivatedUsers = new Map<id, User>();
//Check all users in trigger, first if the IsActive button has been newly changed and if it now says that the user is not active    
for (Integer i=0;i<Trigger.new.size();i++) {
    
if (Trigger.new[i].IsActive!=Trigger.old[i].IsActive &&
   Trigger.new[i].IsActive == false) {
	DeactivatedUsers.put(Trigger.new[i].Id, Trigger.new[i]);
   }
}
// Make sure that there are users to be processed
if (DeactivatedUsers.size() > 0) {

            BatchUpdateLeads objBatchUpdateLeads=new BatchUpdateLeads(DeactivatedUsers);
            Database.executeBatch(objBatchUpdateLeads);
}
}


Batch Apex Class:

global class BatchUpdateLeads implements Database.Batchable<SObject> {
   
        //Save a public variable to hard-code the catchall user that will become lead owner
  	public Id GenericUserId = '0054P000009pLkrQAE';
    
    //map of userid - user that retrieves deactivated user(s) from previous trigger
    Map<Id, User> DeactivatedUsersMap = new Map<Id, User>();
    
    global BatchUpdateLeads(Map<Id, User> DeactivatedUsers) {
    DeactivatedUsersMap = DeactivatedUsers;
    }
    
//Search for the leads whose OwnerId is the deactivated user(s)
    global Database.QueryLocator start(Database.BatchableContext BC) {
    return DataBase.getQueryLocator([SELECT Id, OwnerId FROM Lead WHERE OwnerId IN : DeactivatedUsersMap.keySet()]);
    }
    
    //For leads whose owner was deactivated, change OwnerId to the catchall user
    global void execute(Database.BatchableContext BC, List<sObject> scope){
              List<Lead> leads = (List<lead>)scope;
                for (Lead l: leads){
                    l.OwnerId = GenericUserId;}
        
//Check that there are leads to be updated, then update
            if (leads.size() > 0){
              update leads;
            }
    	}
    global void finish(Database.BatchableContext BC) {
        
    //Send an email to system admin after the batch completes (fill in email)
    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    String[] toAddresses = new String[] {'xxxxxxx@gmail.com'};
    mail.setToAddresses(toAddresses);
    mail.setSubject('Apex Batch Job is done');
    mail.setPlainTextBody('The Batch Apex Job Processed Successfully');
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
}
Best Answer chosen by Sierra VP
Greg HGreg H
Your question(s) make sense.
  1. The User trigger will kick off the BatchUpdateLeads logic each time the trigger is executed. Salesforce typically processes 200 records at a time. So if you updated less than 200 Users to Inactive then it will fire once. More than 200 and it will fire once per chunk of 200 records. It is possible to increase and decrease the number of records being processed but in general your assumption about the logic firing once is valid.
  2. As long as the Apex Flex Queue is active for the Org then, yes, when your BatchUpdateLeads logic is started and is set to a holding status then it will be added to the queue. So you should be permitted up to 100 jobs in that queue.
In general, I don't recommend kicking off a batch Apex job from Trigger. Another approach for your scenario might be a boolean (checkbox) on the User record that gets updated by your trigger in real-time. Then schedule the batch class to run at night and process the Users that have the flag set and remove the boolean designation to denote the processing is complete. The reason for this approach would be to prevent User disruption while changing ownership on so many Leads during business hours.
-greg

All Answers

Greg HGreg H
Your question(s) make sense.
  1. The User trigger will kick off the BatchUpdateLeads logic each time the trigger is executed. Salesforce typically processes 200 records at a time. So if you updated less than 200 Users to Inactive then it will fire once. More than 200 and it will fire once per chunk of 200 records. It is possible to increase and decrease the number of records being processed but in general your assumption about the logic firing once is valid.
  2. As long as the Apex Flex Queue is active for the Org then, yes, when your BatchUpdateLeads logic is started and is set to a holding status then it will be added to the queue. So you should be permitted up to 100 jobs in that queue.
In general, I don't recommend kicking off a batch Apex job from Trigger. Another approach for your scenario might be a boolean (checkbox) on the User record that gets updated by your trigger in real-time. Then schedule the batch class to run at night and process the Users that have the flag set and remove the boolean designation to denote the processing is complete. The reason for this approach would be to prevent User disruption while changing ownership on so many Leads during business hours.
-greg
This was selected as the best answer
Sierra VPSierra VP
Hi @Greg H, I appreciate the feedback, both on my questions and the questionable strategy of using a trigger to launch the batch job. Isn't it also a cumbersome approach though to query for something every night that may or may not happen? I'm just wondering if there's more efficient way. But as you mentioned, triggering a batch job in the middle of the workday could take up a lot of system performance as well.
Greg HGreg H
Scheduling a batch job to run daily is probably the most efficient way to handle your particular use case. If no records get returned in the query executed in the start method then the logic just stops. This is because the execute method requires a list of sObjects in order to run. So your batch kicks off on schedule, queries for records that meet your conditions and either handles those or stops if no records are found. Because it is a batch job it will only start when resources are available anyway.
-greg
Sierra VPSierra VP
That makes sense. Thanks so much for the extra help!