In this article, we will discover what a recursive trigger is and how to avoid it in a trigger. Recursive triggers are a problem for many developers. For instance, the developer performs an update operation in the "after update" trigger, which causes a recursive call.
What is a recursive Trigger?
Recursion is the process of executing the same Trigger multiple times to update the record again and again due to automation. There may be chances we might hit the Salesforce Governor Limit due to Recursive Trigger.
How to avoid recursion in triggers?
There are different ways to solve the recursion in the trigger.
Use Static Boolean Variable
Use Static Set to Store Record Id.
Use Static Map
Use Old Map
Follow Best practices for triggers
Let's take a closer look at all the solutions and their limitations.
Use Static Boolean Variable
You can create a class with a static Boolean variable with a default value of true. In the trigger, before executing your code keep a check that the variable is true or not. Once you check, make the variable false.
Apex Class with Static Variable
public class ContactTriggerHandler{
public static Boolean isFirstTime = true;
}
Trigger Code
Trigger ContactTriggers on Contact (after update){
Set<String> accIdSet = new Set<String>();
if(ContactTriggerHandler.isFirstTime){
ContactTriggerHandler.isFirstTime = false;
System.debug('---- Trigger run ---->'+Trigger.New.size() );
for(Contact conObj : Trigger.New){
if(conObj.name != 'Test') {
accIdSet.add(conObj.accountId);
}
}
// any code here
}
}
This is good for less than 200 records. But what about if we have a large set of records it won’t work. It will update the first 200+ records then it will skip the others. Let’s execute the above code with more than 200+ records.
List<Contact> listCont =[Select id, firstname from contact limit 400];
update listCont;
System.debug('-listCont.size--->'+listCont.size());
Suppose, we have 327 contacts. Let’s query them all and update them. In this case, the trigger should execute 2 times. However due to static variables, the trigger will only execute once and will skip the remaining record from processing.
Check the Salesforce documentation for Apex trigger best practices for using static boolean variables in Apex classes to avoid recursion. Static Boolean is an anti-pattern and should not be used.
Use Static Set or Map
Therefore, it is recommended to use a static set or map to store all running record ids. So the next time you run it again, you can check if the record has already run. Let's modify the above code using Set.
public class ContactTriggerHandler{
public static Set<Id> setExecutedRecord = new Set<Id>();
}
Let us use the same set variable in Trigger.
trigger ContactTriggers on Contact (after update){
System.debug('---- Trigger run ---->'+Trigger.New.size() );
for(Contact conObj : Trigger.New){
if(ContactTriggerHandler.setExecutedRecord.contains(conObj.id)){
if(conObj.name != 'Test') {
// logic
}
}
}
// any code here
}
Let’s test the above code with more than 200 records. Execute the same script and see the result.
Check the Salesforce documentation for Apex trigger best practices for using static boolean variables in Apex classes to avoid recursion. Static Boolean is an anti-pattern and should not be used.
Use Static Set or Map
Therefore, it is recommended to use a static set or map to store all running record ids. So the next time you run it again, you can check if the record has already run. Let's modify the above code using Set.
public class ContactTriggerHandler{
public static Set<Id> setExecutedRecord = new Set<Id>();
}
Let us use the same set variable in Trigger.
trigger ContactTriggers on Contact (after update){
System.debug('---- Trigger run ---->'+Trigger.New.size() );
for(Contact conObj : Trigger.New){
if(ContactTriggerHandler.setExecutedRecord.contains(conObj.id)){
if(conObj.name != 'Test') {
// logic
}
}
}
// any code here
}
Let’s test the above code with more than 200 records. Execute the same script and see the result.
Use Static Set to avoid recursion in Trigger
The use of a static Set is less than ideal because there are plenty of times when you may get both an insert and an update within the same transaction (esp. with a mix of the apex and flow logic).
Use Static Map
You can also use a static card to store the event name and id like this:
public static Map<String,Set<Id>> mapExecutedRecord = new Map<String,Set<Id>>;
Use it like below
if(!ContactTriggerHandler.mapExecutedRecord.containsKey('afterUpdate')){
ContactTriggerHandler.mapExecutedRecord.put('afterUpdate',new Set<ID>());
}
....
for(){
if(ContactTriggerHandler.mapExecutedRecord.get('afterUpdate').contains(conObj.id)) {
}
}
In this way, you can handle all events using the same map.
Use Old Map
Always use the old map to compare values before executing trigger logic. Here's an example of how to compare old values to resolve trigger recursion.
for(Opportunity opp:Trigger.New){
if(opp.Stage != Trigger.oldMap.get(opp.Id).Stage){
//Your logic
}
}
In the case of OldMap, your code will only execute once the value will change.
Follow Best practices for triggers:
Follow Trigger best practices and use the Trigger framework to resolve the recursion.
One trigger per object so you don’t have to think about the execution order as there is no control over which trigger would be executed first.
Logic-less Triggers – use Helper classes to handle logic.
Code coverage 100%
Handle recursion – To avoid the recursion on a trigger, make sure your trigger is getting executed only one time. You may encounter the error: ‘Maximum trigger depth exceeded’, if recursion is not handled well.
Use Apex Code best practices in Salesforce to design the better Trigger.
Summary:
Recursive triggers occur when a trigger repeatedly calls itself, leading to potential issues such as hitting Salesforce Governor Limits. To avoid recursion, developers can implement several solutions, including:
Static Boolean Variable: Simple but limited to smaller datasets.
Static Set/Map: More effective for larger datasets by tracking record IDs or event names.
Old Map: Comparing old and new values to prevent unnecessary updates.
Best Practices: Adopting a trigger framework, using helper classes, maintaining one trigger per object, and ensuring logic-less triggers to handle recursion efficiently.
To prevent errors like ‘Maximum trigger depth exceeded,’ it is crucial to design triggers thoughtfully using best practices, especially for handling recursion.
Enhance your Salesforce development skills by mastering the art of building efficient triggers. Contact us to learn more about Salesforce best practices and optimize your development process today!
Comentários