5

I have a simple Contact trigger in a sandbox org that is exhibiting some serious performance issues. After doing some debugging it looks like the issue boils down to accessing the items inside Trigger.new list. I mean literally just doing o = Trigger.new[i] appears to be really slow.

For example, this code runs reasonably quickly (processing 1000 updates in about 30 seconds):

trigger MyTrigger on Contact (after insert, after update, before delete) {
    if (Trigger.isUpdate) {
        for (Integer i = 0; i < Trigger.new.size(); i++) {
            Integer dummy = 0;
        }
    }
}

Results from bulk data load (coming from the bulk job details page):

Total Processing Time (ms): 29370
API Active Processing Time (ms): 27229
Apex Processing Time (ms): 28852

However, this code takes about six minutes to run:

trigger MyTrigger on Contact (after insert, after update, before delete) {
    if (Trigger.isUpdate) {
        for (Integer i = 0; i < Trigger.new.size(); i++) {
            SObject o = Trigger.new[i];
        }
    }
}

Results from bulk data load:

Total Processing Time (ms): 321264
API Active Processing Time (ms): 319187
Apex Processing Time (ms): 893650

If I record the time required to execute SObject o = Trigger.new[i] using System.currentTimeMillis() I see a lot of times around 400 milliseconds.

What's going on here? Does it really take that long to access an element in a list? Is there any way around this? I thought maybe I could convert it to an actual array but apparently toArray() is not a method that the Salesforce version of List supports.

I also find it odd the the Apex Processing Time value is greater than the Total Processing Time value but maybe that's a separate issue.

Update 1

This is pure speculation, but this org has a lot of customizations on the Contact record, including several formula fields. I'm wondering if, as a performance optimization, you aren't working with a "live" Contact record in the trigger until you access it from the Trigger.new list. Maybe it's taking a while to pull the Contact record out of Salesforce's database and compute all the formula fields on the fly. Just a theory...

Update 2

Somebody suggested that I try this:

trigger MyTrigger on Contact (after insert, after update, before delete) {
    if (Trigger.isUpdate) {
        for (Integer i = 0; i < Trigger.new.size(); i++) {
            if (Trigger.new[i] == null) {
            }
        }
    }
}

So the code is still access the List it's just not assigning it to anything. Doing this does address the performance issue (though it's not useful code). Not 100% sure why that is, something about the assignment to a variable is causing the issue. Maybe a copy of some sort is occurring? Anyway, it's another data point.

9
  • Where are you getting these numbers?
    – Adrian Larson
    Feb 5, 2019 at 23:50
  • 2
    @AdrianLarson - (presumably) Source of the statistics: View Bulk Data Load Job Details
    – Mark Pond
    Feb 6, 2019 at 0:09
  • @AdrianLarson those numbers are from the Bulk Data Load jobs results page.
    – d512
    Feb 6, 2019 at 15:43
  • 1
    Sandboxes are lot slower than prod, might be that Feb 6, 2019 at 15:59
  • 1
    @d512 have you enabled Skinny Tables in your org? Also worth raising a case with SF Feb 6, 2019 at 20:18

1 Answer 1

1

Here is my test class for 2000 records in a new playground.

@IsTest
private class ContactTriggerTest {
   @IsTest
   public static void triggerTest(){
        Integer totalNumber = 2000;
        List<Contact> contactList = new List<Contact>();
        for(Integer i=0;i<totalNumber;i++){
            contactList.add(new Contact(FirstName='Felix' + i, LastName='L'));
        }

        Insert contactList;

        for(Integer i=0;i<totalNumber;i++){
            contactList[i].FirstName='Updated '+contactList[i].FirstName;
        }

        Update contactList;

        System.assertEquals(1, 1);
   }
}

27 sec

if (Trigger.isUpdate) {
        for (Integer i = 0; i < Trigger.new.size(); i++) {
            Integer j=i;
        }
    }

28 sec

if (Trigger.isUpdate) {
        SObject o = null;
        for (Integer i = 0; i < Trigger.new.size(); i++) {
            o = Trigger.new[i];
        }
    }

29 sec

if (Trigger.isUpdate) {        
        for (Integer i = 0; i < Trigger.new.size(); i++) {
            SObject o = Trigger.new[i];
        }
    }

As you can see it not much big diffidence, must be something else.

You must log in to answer this question.

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