15

What are recursive triggers? How can we control them, or prevent infinite recursion?

Are these any risks inherent in them, and any limits that we should be aware of?

Please explain with an example.

30

What is a Recursive Trigger?

A recursive trigger is one that is called over and over, if not controlled will result in this error...

maximum trigger depth exceeded

This is a simple way to create this error via a trigger on an object called Test__c...

trigger TestTrigger on Test__c (before insert) {
    insert new Test__c();
}

With the above trigger in place, when one Test__c object record is created by the user, the trigger executes and then inserts another record of the same type, which causes the trigger to execute again for this record, this keeps repeating until the platform stops things itself. As you can see by this screenshot the platform tolerates up to 8 levels before it stops.

This is what the end user sees eventually...

enter image description here

I have expanded out the error to make it easier to read below...

Review all error messages below to correct your data.

Apex trigger TestTrigger caused an unexpected exception, contact your administrator:

TestTrigger: execution of BeforeInsert caused by: System.DmlException:

Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, TestTrigger: maximum trigger depth exceeded

Test trigger event BeforeInsert for [new] Test trigger event BeforeInsert for [new]

Test trigger event BeforeInsert for [new] Test trigger event BeforeInsert for [new]

Test trigger event BeforeInsert for [new] Test trigger event BeforeInsert for [new]

Test trigger event BeforeInsert for [new] Test trigger event BeforeInsert for [new]

Test trigger event BeforeInsert for [new] Test trigger event BeforeInsert for [new]

Test trigger event BeforeInsert for [new] Test trigger event BeforeInsert for [new]

Test trigger event BeforeInsert for [new] Test trigger event BeforeInsert for [new]

Test trigger event BeforeInsert for [new] Test trigger event BeforeInsert for [new]: []: Trigger.TestTrigger: line 2, column 1

This also applies to other DML operations such as updating or deleting from the same object. Typically you encounter this scenario when you have some kind of hierarchy relationship within your object, such as a parent child and you need to update parent records when child records are effected. There is a good summary in the Context Variables Considerations of what can and cannot be done.

Avoiding Recursive Triggers

The Apex Developers Guide surprisingly does not say much about this, aside from a reference in the Static and Instance topic.

Use static variables to store information that is shared within the confines of the class. All instances of the same class share a single copy of the static variables. For example, all triggers that are spawned by the same transaction can communicate with each other by viewing and updating static variables in a related class. A recursive trigger might use the value of a class variable to determine when to exit the recursion.

Suppose you had the following class:

public class p { 
   public static boolean firstRun = true; 
}

The above example would be adapted to use this approach as follows...

trigger TestTrigger on Test__c (before insert) {
    if(p.firstRun)
    {
        p.firstRun = false;
        insert new Test__c();
    }    
}

Static variables defined in a trigger don’t retain their values between different trigger contexts within the same transaction, for example, between before insert and after insert invocations. Define the static variables in a class instead so that the trigger can access these class member variables and check their static values.

With this approach in place the recursion stops once the trigger is called a second time, as it does not attempt to insert a third record. The result is for every one record a second additional record is created and the recursion is avoided.

Warning: Any form of recursive coding is by definition complicated, as this blog I found illustrates the use of a static flag recording if the trigger has previously been executed can be to simplistic for some use cases, if you don't fully consider your use cases.

  • 3
    +1 Nice Answer! You also have to watch out for Lists with more than 200 elements. The static variable won't reset, so only the first 200 elements will get processed. See the test scenario set up in this question with a nice sol'n by @ca_peterson. – Peter Knolle Jan 17 '14 at 2:00
  • 1
    Thanks Peter, the answer evolved quite well, i'm pleased with it. – Andrew Fawcett Jan 17 '14 at 19:14
  • I think it's not 8 levels, it's 16. (You have 2 "Test trigger event BeforeInsert for [new]" messages per line in your expanded error.) – Legolas Aug 21 '14 at 13:33

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