How to avoid recursion in Trigger

How to avoid recursion in Trigger

In this post, we will learn about what is a recursive trigger and how to avoid the recursion in a Trigger. Many Developers face recursive triggers issues. For example in the ‘after update’ trigger, the Developer is performing an update operation, and this lead to the 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.

Recursive trigger example

The below example shows what is a Recursive Trigger and how it can be avoided.

How to avoid recursion in trigger?

There are different ways to solve the recursion in the trigger.

  1. Use Static Boolean Variable
  2. Use Static Set to Store Record Id.
  3. Use Static Map
  4. Use Old Map
  5. Follow Best practice for triggers

Let’s take a look in detail at all solutions and their limitations.

1. 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());

In my org, we have 327 contacts. Let’s query them all and update them. In this case, the trigger should execute 2 times. But due to static variables trigger will only execute once and will skip the remaining record from processing.

Use Static Boolean Variable to avoid recursion in Trigger

Check Salesforce document for Apex Trigger Best practices to avoid recursion by using Static Boolean variable in apex class. Static Boolean is an anti-pattern so please DO NOT USE.

2. Use Static Set or Map

So It is better to use a Static set or map to store all executed record ids. So when next time it will execute again we can check record is already executed or not. Let modify the above code with the help of Set.

public class ContactTriggerHandler{
     public static Set<Id> setExecutedRecord = new Set<Id>();
}

Let us 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
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).

3. Use Static Map

You can also use Static map to store the event name and set of Id like below.

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 event using same map. Check out this thread.

4. Use Old Map

Always use Old map to compare the values before executing the trigger logic. Here is example how we can compare old value to resolve the recursion in Trigger.

for(Opportunity opp:Trigger.New){
	if(opp.Stage != Trigger.oldMap.get(opp.Id).Stage){
		//Your logic
	}
}

In case of OldMap your code will only execute once value will change.

5. Follow Best practice for triggers

Follow Trigger best practices and use the Trigger framework to resolve the recursion.

  1. 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.
  2. Logic-less Triggers – use Helper classes to handle logic.
  3. Code coverage 100%
  4. 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

There are different ways to handle recursive triggers in triggers. But I highly recommend using Trigger Framework with a static set to avoid the recursion in the trigger.

Amit Chaudhary

Amit Chaudhary

Amit Chaudhary is Salesforce Application & System Architect and working on Salesforce Platform since 2010. He is Salesforce MVP since 2017 and have 17 Salesforce Certificates. He is a active blogger and founder of Apex Hours.

Share this article

4 Comments

  • good

  • All of your examples forget about the possibility of a partial update being blocked because of a retry. You should always set the flag or Set values when starting a trigger, then reset/remove those values at the end of the trigger. Even better, using a technique like this, you can be more selective with your recursion blocking.

    See my post: https://salesforce.stackexchange.com/a/296817

    • Great idea, We include that in point 2 for using the map.

Leave a reply

Subscribe for Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 4,092 other subscribers

Our Supporter

RECENT POSTS

Apex Hours

Apex Hours is one stop platform to learn Salesforce skills and technology

Join our Newsletter and get tips and tricks how to explore the salesforce for free!