[SalesForce] Question regarding “RunOnce” Booleans on AfterUpdate methods in a Salesforce Transaction

I recently encountered a business logic bug in our org that was accidentally deducting a customer 2x for completing an event that only actually occurred once. Using dev console I was able to determine that the method was running twice as a result of a Workflow Field Update that caused the transaction to loop through the order of execution a second time. Unchecking the "re-evaluate" box on the workflow rule didn't stop the bug.

So, I went the route of creating a 'RunOnce' style Boolean in a Helper Class, checking if it is false at the start of the method, and then setting it to true as the last line in the entire method (even after DML updates). This of course doesn't stop the record(s) from going through order of execution again, but it does stop the method from running since it already ran.

Example with main business logic removed for size purposes:

public static void updateUsedTrainingTimeUsingEventMaps(Map<Id,Event> oldEventMap, Map<Id,Event> newEventMap){
    if(ValidatorClass.UpdateTrainingTimeTriggerFired == false){ // check if this method has run already

        for(Event event : newEventMap.values())
        {
            A BUNCH OF BUSINESS LOGIC, FIELD UPDATES, ADDING TO UPDATE LISTS, ETC
        } // end of for loop

    update accountMap.values();
    ValidatorClass.UpdateTrainingTimeTriggerFired = true; // stop method from running again in same transaction

} // end of updateUsedTrainingTimeUsingEventMaps()

Question: Being that this is my first time doing this, I am unsure of
how exactly this effects (if at all), mass updates that trigger this
method. I learned recently (in SFDev450 class) that Salesforce handles
records in sets of 200 in loops – but I am unsure if that means they also only pass 200 records to the method at once.

Does this mean:

  1. Each 200 records get their own transaction per 200, and by extension, a fresh reset of the Boolean?

OR

  1. Will my 201st record not run the method since it's a second set of
    200 being run through the method in the same transaction where the
    Boolean is now true?

OR

  1. Will all of my records (let's say 405) be sent to the method at the
    same time but just run through the loops in sets of 200 before doing
    my final DML update outside of the loop, resulting in all 405 running the method once regardless of how many times subsequent order of executions may start in that transaction?

OR

  1. None of the above? (Please specify)

Many thanks for your insight!

Best Answer

Very astute question! The answer is number 2. You want to put your boolean in your trigger where the class is called from. You'll need a trigger utility class to hold the boolean. This is a large part of why trigger platforms are so useful, particularly ones that have a "trigger main" that acts as a distribution class that follows the logic of a regular base trigger from which your helper classes are called.

Example of Utility class:

Public class TrggrUtility{

   public static boolean RunOnce = False;

}

Test inside of trigger:

If(!TrggrUtility.RunOnce) 
{
   // do... call method
   TrggrUtility.RunOnce = true;

}else{

  // exit trigger or code section

}
Related Topic