Consider the following:

Sequence A:

  1. User performs update on a record

  2. SObject trigger fires

  3. Workflow updates the same record

  4. SObject trigger fires again

A few seconds later, sequence B:

  1. User performs update on the same record

  2. SObject trigger fires

Step A.4 is unwanted recursion. A typical way to resolve this is to use a static class variable as per https://help.salesforce.com/articleView?id=000133752&type=1

However if B starts before A is completed then B.2 will also be inadvertently suppressed. I'm looking for a way to avoid this.

A set of static booleans seems the obvious way to address multiple executions, but I haven't identified a key value that would be the same for A.2 and A.4 but different for B.2.

i.e. what key to pass in from the trigger to

public class RecursionCheck {
  private static Set<String> recursionSet = new Set<String>();
  public static Boolean runOnce(String key) {
    if (recursionSet.contains(key)) { return false;
    } else {
      return true;

Failed key candidates so far:

  1. Record Ids. All are the same

  2. Datetimes. A.2 & A.4 are different

  3. Checksum - AES encoded trigger.new + trigger.old. A.2 & A.4 are different because different fields are being updated

What I really need is something like a "debug log id" which would be common to both A.2 & A.4 but different for B.2, but I haven't found anything like that.

Standard pages & buttons are in play so I don't see an easy way to display a "please wait" message for sequence B to prevent concurrent execution.

And for reasons too convoluted to go into, I can't move the workflow logic into the trigger.


I think you could benefit from a trigger framework, like the one Kevin O'Hara built.

Using a framework like this you will have a single class to handle your Sobject trigger events, so you won't need more than one trigger on that object. Every event will be handled by the same class, which means that you can control the execution order on the trigger context.

Using the bypass API you can also avoid recursivity.

If you don't want to use a framework, you can just follow this logic of creating one trigger per object and handling the logic in a class.


Not entirely an answer, but too long for a comment...

Unless I'm mistaken, static variables in Salesforce last for the life of a transaction and are not visible to each other across transactions. So, A and B would never see each other's static variables, even if they overlap in time.

Also, static variables should be considered a last resort for a preventing unwanted recursion. Ideally, you should look at the old/new trigger context (maybe do a query or two if you have two), then just exist the trigger if there is not work to be done. This is not always possible, but if you use a set of ids in a static variable, then you expose yourself to the possibility of a change in the system causing problems later. Suppose someone adds more triggers/workflows to the system which means that you trigger gets run twice and it really does need to redo the processing the second time? If you just exclude the records on the basis of id, then this will be a problem.

You seem to already know that in your scenario A, the old list in the trigger for step 4 will be the original old list from step 1 which is quite tricky!

Trigger.old contains a version of the objects before the specific update that fired the trigger. However, there is an exception. When a record is updated and subsequently triggers a workflow rule field update, Trigger.old in the last update trigger won’t contain the version of the object immediately prior to the workflow update, but the object before the initial update was made. For example, suppose an existing record has a number field with an initial value of 1. A user updates this field to 10, and a workflow rule field update fires and increments it to 11. In the update trigger that fires after the workflow field update, the field value of the object obtained from Trigger.old is the original value of 1, rather than 10, as would typically be the case.


  • Thanks for your input, it looks like my diagnosis of my problem is wrong so I will need to state a new question to get the result I need. – 123 Jul 29 '17 at 1:30

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.