0

I have an object that gets its record type changed by the user and based on that change I want to create some new records of another object.

So in the trigger I compare Trigger.old to Trigger.new and voilà - I have my condition and everything works fine. That is until I introduce some workflow rules to the party that update the record itself during the record update. This leads to duplicated records.

The Apex Developer Guide has an interesting paragraph under "Triggers and Order of Execution".

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.

So the workflow rule fires the trigger a second time but since the record type is not changed (and will not be changed) yet, the condition is met again and thous I get another copy of the record I want.

My current workaround is a static variable and I only insert my new records the first time my criteria are met. This works as long as I'm not going to (for whatever reason) update my record twice in a row - because then I would get the trigger only once, not twice. So apparently this works but I think it is a pretty bad "solution".

Has anybody else stumbled across this problem? How did you fix or work around it?

UPDATE:

I did a few tests to help others and myself to understand the problem better. So here are my findings explained through a simple example.

Case 1: Update on object A (one update, one increment)

  1. Trigger on object A fires --> Trigger increments field on object B (+1)

Workflow rule is added to object A that updates itself.

Case 2: Update on object A (one update, two increments)

  1. Trigger on object A fires --> Trigger increments field on object B (+1)
  2. Workflow trigger on object A fires --> Trigger increments field on object B (+1)

Case 3: Update on object A - with static variable firstRun set to false (one update, one increment)

  1. Trigger on object A fires --> firstRun true, set to false --> Trigger increments field on object B (+1)
  2. Workflow trigger on object A fires --> firstRun false, no increment on object B

Trigger logic is introduced on object B that does an update on object A if it hasn't been done before.

Case 4: Update on object A (two updates, four increments)

  1. Trigger on object A fires --> Trigger increments field on object B (+1)
  2. Workflow trigger on object A fires --> Trigger increments field on object B (+1)
  3. Object B triggers update on object A
  4. Trigger on object A fires --> Trigger increments field on object B (+1)
  5. Workflow trigger on object A fires --> Trigger increments field on object B (+1)

Case 5: Update on object A - with static variable firstRun set to false (two updates, one increment)

  1. Trigger on object A fires --> firstRun true, set to false --> Trigger increments field on object B (+1)
  2. Workflow trigger on object A fires --> firstRun false, no increment on object B
  3. Object B triggers update on object A
  4. Trigger on object A fires --> firstRun still false --> nothing happens

So we want to make sure that our increment count is the same as our update count. This only works for case 1 (no workflow rules at all) and case 3 (workflow rules but no further triggering of the same "source" update). What we really want is to have N updates and N increments - they should always be the same. But that isn't the case for example number 5.

I know this case is probably very rare but in our orgs there's a lot of ping-pong going on at times - hence the problem. You can easily test the behaviour with two updates directly after one another (or the first one triggering the second one).

For now I have no solution beside the static variable workaround plus preventing multiple updates in the same execution context, if possible - circumventing the problem as good as I can.

As one of the respondents pointed out: If you're in control of the triggering then you can reset firstRun in between the updates. If you're not, you're out of luck. Sometimes Salesforce is making our lives really REALLY hard.

1

2 Answers 2

1

The static Boolean variable tends to introduce bugs, but you can use a set instead:

static Set<Id> processedIds = new Set<Id>();
public static Boolean firstRun() {
  Boolean result = !Trigger.newMap.keySet().containsAll(processedIds);
  processedIds.addAll(Trigger.newMap.keySet());
  return result;
}

This avoids the 200+ limitation of a Boolean at some cost to heap size.

1
  • Thanks for your feedback. So this fixes just the limitation right? And I'm not quite sure how this solution would introduce less bugs - can you please explain?
    – Semmel
    Jul 22, 2016 at 22:37
0

You want to use a static variable in a trigger handler to prevent execution more than once. Something like this:

public class ObjectTriggerHandler {
  public static boolean firstRun = true;
  public static void onAfterInsert(List<Object__c> newRecords, List<Object__c> oldRecords) {
    if (!firstRun) {
      return;
    } else {
      firstRun = false;
    }

    // normal execution here
  }
}
5
  • I see you mentioned this in your question. If you updated your record twice in a row, it would still execute as long as it's not in the same execution context. You can always control this flow at any time by saying ObjectTriggerHandler.firstRun = true; // then do logic
    – Ray Dehler
    Jul 20, 2016 at 0:01
  • But if I do it in the same execution context - I'm screwed. Right?
    – Semmel
    Jul 20, 2016 at 0:18
  • No. If you do 2 explicit inserts, you could just reset the firstRun in between as I said in my other comment.
    – Ray Dehler
    Jul 20, 2016 at 0:19
  • N.B. the static variable boolean works well as long as no client ever inserts more than 200 records in a single DML call. If 201 records are inserted, SFDC will execute the trigger on rows 1-200, the boolean becomes a sticky false and when SFDC presents record 201 to the trigger, record 201 will never be processed by the handler.
    – cropredy
    Jul 20, 2016 at 1:26
  • First of all - thanks for your feedback. If I'm the one doing the inserts then I could reset firstRun in between - unfortunately that isn't the case ... and it works as I suspected. So this solution is sufficient if you can make sure that only one update (the workflow update aside) will happen in any single execution context. If you're able to control the trigger during the execution you could just reset firstRun. If you have multiple updates happening and can't control your operations - you're screwed and you probably need a much more complicated solution.
    – Semmel
    Jul 22, 2016 at 22:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .