3

I'm looking to optimise my code because I keep getting the error:

Apex CPU time limit Exceeded

I understand this error, which means your apex code cannot be running for more than 10 seconds on the salesforce servers.

Now I have fixed my problem and I am no longer receiving the error, but when I analyse my code in the debug logs, I can see the Apex code is running for 34 seconds, so how is this not erroring?

For reference, here is my code:

public class TestApexCpuLimitVFPageController {
    static List<Account> newAccounts = new List<Account>();    
    static List<Contact> newContacts = new List<Contact>();
    
    static Map<string, Account> newAccountsMapByName = new Map<string, Account>();
    
    public static void test()
    {
        Savepoint sp = Database.setSavepoint();
        createAccounts();
        createContacts();
        
        matchContactWithAccount();
        Database.rollback(sp);
    }
    
    static void matchContactWithAccount()
    {
        integer i = 0;
        for (Contact c: newContacts)
        {
            Account acc = getAccountByName('test' + i);
            //Account acc = newAccountsMapByName.get('test' + i);
            i++;
            
            c.AccountId = acc.Id;
        }
        
        update newContacts;
    }
    
    static Account getAccountByName(string accountName)
    {
        for (Account acc: newAccounts)
        {
            if (acc.Name == accountName)
            {
                return acc;
            }
        }
        
        return null;
    }
    
    static void createContacts()
    {        
        for (integer i = 0; i < 500; i++)
        {
            Contact newContact = new Contact(FirstName='first' + i, LastName='last' + i);
            newContacts.add(newContact);
        }
        
        insert newContacts;
    }
    
    static void createAccounts()
    {        
        for (integer i = 0; i < 500; i++)
        {
            Account newAccount = new Account(Name='test' + i);
            newAccounts.add(newAccount);
            newAccountsMapByName.put('test' + i, newAccount);
        }
        
        insert newAccountsMapByName.values();
    }
}

execution overview screenshot

5
  • Wait a minute where are you taking this time measurement from? Sorry, I see it debug logs. Commented Aug 24, 2022 at 14:08
  • @BorisGichev in my screenshot, I'm looking at the Timeline tab, and "APEX_CODE" shows 34,000 in the Millis column
    – ViqMontana
    Commented Aug 24, 2022 at 14:10
  • Hmmm I would not trust this counter :) In your code, start a time and print it in debug message at the end of the main method test(). Commented Aug 24, 2022 at 14:12
  • Use DateTime.getTime() at the start to save milliseconds and then subtract to another DateTime.getTime() result at the end of the method. Commented Aug 24, 2022 at 14:15
  • Not only can you use Test.startTest() and Test.stopTest() in the regular test methods, but in addition to using it there, you can also use Test.startTest() and Test.stopTest() in the TestSetup method to gain an extra set of governor limits. I have found this technique to be invaluable to avoid governor limits in test classes. Put differently, you can use Test.startTest() and Test.stopTest() in both the TestSetup method and the regular test methods at the same time to gain an extra set of governor limits during the tests. Commented Jun 13 at 18:34

1 Answer 1

7

34 seconds is the "wall clock" time calculated from the log, which is not reflective of what you're getting "charged" for in the CPU limit. Actual limit usage will be less than that. I usually use a System.assert(false, Limit.getCpuTime()) at the end of my code if I want to produce output without a log.

34 seconds is far too much time for this to be running normally. If you tone down the debug settings to:

55.0 APEX_CODE,NONE;APEX_PROFILING,FINEST;CALLOUT,NONE;DB,NONE;NBA,NONE;SYSTEM,NONE;VALIDATION,NONE;VISUALFORCE,NONE;WAVE,NONE;WORKFLOW,NONE

You should end up with a much more reasonable:

External entry point: static testMethod void test(): executed 1 time in 25009 ms

But, that still comes around to why this code didn't crash. After all, it took 25 seconds to run, right? That only accounts for the wall clock time. Scrolling up a little, it looks like I actually used far less:

Maximum CPU time: 13244 out of 10000 ******* CLOSE TO LIMIT

Time spent in the database and callouts doesn't count for CPU usage, but does count in real time ("wall clock time").

Note that the CPU limits are flexible. You're only guaranteed 10,000 ms, but it is not unusual to see values as high as 15,000 in production not fail, and I've observed values as high as 20,000 in Sandboxes. It depends on how heavy the system load is; during busy times, the limits are more strictly enforced.

Obviously, you should try to optimize below 10,000 CPU time used, because even at 5 seconds you can end up tripping over the long transaction limit (10 concurrent requests over 5 seconds).

You must log in to answer this question.

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