[SalesForce] Handling trigger recursion in bulk data load

  1. Update 405 records on an object (So it executes the trigger 3 times 200+200+3 i.e, each batch holds 200 records)
  2. There is a process builder which in turn is updating the same object (Will fire the trigger again)
  3. Workflow field update – will fire the trigger again)

So in this example, trigger will run 6*3 times (i.e., per batch it will execute 6 times – 3 before and 3 after) but we need the trigger to execute only 2 times (1 before and 1 after)

i.e., triggers should not execute when process builder or work flow field update updates the record

Recursive Static Variable: Will work only for first 200 records

Best Answer

This is a great example of why so-called recursion protection like this:

public class MyTriggerHandler {
    static Boolean hasAlreadyRun = false;
}

trigger MyTrigger on MyObject__c (after update) {
    if (!MyTriggerHandler.hasAlreadyRun) {
        // Do stuff
        MyTriggerHandler.hasAlreadyRun = true;
    }
}

is a terrible pattern.

Without seeing more of your code, you probably have two options.

The first is to fix the recursion. Fold your Process Builder and your Workflow Rule into the code of your trigger handler. Honestly, I would do this anyway - if you're doing bulk loads on this data you don't need to have extra trigger invocations taking up transaction time anyway.

The second is to use a static collection-based recursion guard, something like this:

public class MyTriggerHandler {
    static Set<Id> recordsAlreadyProcessed = new Set<Id>();

    public static void afterUpdate(List<MyObject__c> newList, Map<Id, MyObject__c> oldMap) {
        List<MyObject__c> toProcess = new List<MyObject__c>();

        for (MyObject__c m : newList) {
            if (!recordsAlreadyProcessed.contains(m.Id)) {
                recordsAlreadyProcessed.add(m.Id);
                toProcess.add(m);
            }
        }

        // Now process `toProcess` only.
}

trigger MyTrigger on MyObject__c (after update) {
    MyTriggerHandler.afterUpdate(Trigger.new, Trigger.oldMap);
}

Note however that this isn't necessarily a copy-and-paste solution. If your trigger should reprocess records that undergo specific changes from workflow rules, or even depend upon those updates, this pattern will break that. You need to carefully evaluate how your trigger and your declarative automations interact before you add any kind of suppression logic, and be aware that using this pattern does have implications for any future implementations of declarative automation you may do.

Related Topic