4

What is the best way to avoid recursion of trigger ?

Below are the approaches used and problems faced

Using static boolean variable from handler class

Apex Class

public Class checkRecursive{
    private static boolean run = true;
    public static boolean runOnce(){
        if(run){
            run=false;
            return true;
        }else{
            return run;
        }
    }
}

Apex Trigger

trigger updateTrigger on anyObject(after update){
    if(checkRecursive.runOnce()) {
        //write your code here            
    }
}

Using the above approach works only for the first chunk of records i.e. first 200 records, post first 200 records, trigger does not work i.e. lets say, I am trying to insert 400 records, so trigger will segregate this 400 records into 2 chunks and when the first batch runs, variable is set to false and hence for the second batch i.e. next 200 records are not processed (Variable is not reset between chunks).

Using static set variable from handler class

We can use this static set approach, but I am not sure how to handle this when we are working with before insert context i.e. when Id is not generated and when we don't want to check for duplicates.

Came across this help documentation from Salesforce Apex Trigger Best practices to avoid Recursion which shows how to use static set<Id> to prevent recursion, but I suppose the example which is shown is incorrect.

Apex Class

public class checkRecursive {
    public static Set<Id> SetOfIDs = new Set<Id>();
}

Apex Trigger

trigger TestTrigger on Account (before insert) {
    if (Trigger.isAfter && Trigger.isInsert) {
        List<Account> accList = new List<Account>();
        for (Account test: Trigger.new) {
            If(!checkRecursive.SetOfIDs.contains(test.Id)){
                test.Name = 'helloworld'; 
                accList.add(test);
                checkRecursive.SetOfIDs.add(test.Id);
            }
        }
        insert  accList;
    }
}

In the above example from Salesforce help link mentioned above, trigger is registered for before insert event whereas it is checking for after insert context variables i.e. Trigger.isAfter && Trigger.isInsert, so not sure how should it be handled when this check needs to be done for before insert context or Do we really need to check for recursion for before insert context ?

Addition

If we don't add a check for recursion for INSERT event, the trigger will fire multiple times i.e. 2 times in a case where workflow field update fires on record insert which will re-run any before and after insert triggers again—as insert triggers. Triggers and Order of Execution

  • You don't have to worry about recursion in INSERT event. Recursion only comes in UPDATE. The best way is to see has field has changed. – Pranay Jaiswal Aug 6 at 13:10
  • 1
    @PranayJaiswal If I don't add a check for recursion for INSERT event, the trigger will fire multiple times i.e. 2 times in a case where workflow field update fires on record insert which will rerun any before and after insert triggers again—as insert triggers. Triggers and Order of Execution – Vijay Ganji Aug 7 at 6:46
3

If you want to avoid recursion the set of ids is the right way.

  1. Before anything else, filter the chuck of records by their id and keep only the ones not present in the set or that do not have an id yet.
  2. Update the set of ids.
  3. Do anything you have to do in your before trigger.
  4. If your trigger handles also inserts, update again the set of ids
  5. Do anything you have to do in your after trigger.

Note also that:

  • You can use a map if you want to enable the recursion of some records until a certain depth is reached.
  • You can use multiple sets and maps to handle the recursion of different records in different ways.
  • You can avoid to filter the chuck of records if it is a delete trigger to permit the deletion of the records even if it is updated/inserted in the same transaction.
  • "Note that the insert can not cause a recursion of insert triggers. You store the corresponding ids only to avoid the execution of an update trigger on the same records that have been inserted." You refer like when you insert and a process builder or workflow fires? Just to know if thats the target scenario to prevent or which one would it be. Thanks. – Alexander Aeons Torn Aug 5 at 11:27
  • 1
    @AlexanderAeonsTorn By bad, I checked the documentation and if there is any workflow rule on record creation that update any field it will cause the insert trigger to fire again. In this case, however, Trigger.New should already have the record id since it was already added to the DB even if not committed yet. – gvgramazio Aug 5 at 12:17
  • @VijayGanji That was my first thought, but the documentation states the opposite. A workflow field update that fires on record insert will rerun any before and after insert triggers again—as insert triggers. (see note after point 12 in Triggers and Order of Execution) – gvgramazio Aug 5 at 13:20
  • @VijayGanji I know. I think that it is because the insert isn’t committed yet. However, but it is only my speculation, that the id should already be set since the record is already added to the DB. Otherwise, I don’t think it’s possible to avoid recursion in insert. However, keep in mind that the workflow run only once, so the trigger would be called again at most one time. – gvgramazio Aug 5 at 13:36
  • 5
    note that sets of ids won't always work when in AllorNone=false mode and SFDC auto-retries the successes in a partial fail batch. see detailed blog discussion – cropredy Aug 5 at 17:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.