Apex – CPU Time Limit Not Exceeded Despite Code Taking More Than 10 Seconds

apexcpulimitgovernorlimits

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

Best Answer

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).