Introduction
The CPU Time Limit Exception in Salesforce is a common error that occurs when the execution of Apex code exceeds the CPU time limit that is set by Salesforce.
This exception is enforced as part of Salesforce’s governor limits to ensure fair resource usage in its multi-tenant environment.
In this blog, we will explore:
- What is the CPU Time Limit Exception?
- What causes of CPU Time Limit Exception.
- Examples of scenarios that trigger CPU Time Limit Exception.
- Techniques to avoid and handle this exception.
- Best practices for optimizing Apex code.
1. What is the CPU Time Limit Exception?
The CPU Time Limit Exception occurs when the total execution time of Apex code exceeds Salesforce’s CPU time limits:
- For Synchronous transactions, the time limit set by Salesforce is: 10,000 milliseconds (10 seconds).
- For Asynchronous transactions, the time limit set by Salesforce is: 60,000 milliseconds (60 seconds).
This time limit applies to the total time spent by the server processing your Apex code, including database operations, calculations, and logic execution.
2. What are the Causes of CPU Time Limit Exception?
a. Inefficient Loops
If your Apex code contains nested or poorly optimized SOQL loops, then it can consume significant CPU time.
b. Redundant SOQL or DML Operations
If your Apex class is having multiple SOQL queries or performing multiple DML operations within loops, then it can also significantly increase execution time which can result in CPU Time Limit Exception.
c. Large Data Volumes
Processing large datasets without using techniques like bulkification or pagination can lead to high CPU usage.
d. Complex Logic
Heavy computations, recursive logic, or unoptimized algorithms can consume excessive CPU time.
e. Recursive Triggers
If your Salesforce org has triggers that call other triggers repeatedly without proper control, then it can result in an infinite loop, causing the CPU limit to be exceeded.
f. Excessive Debugging
If you are using a lot of System.debug() statements in your Apex class, then it can add unnecessary overhead during execution which may results in CPU Time Limit Exception.
3. Examples of Scenarios that cause - CPU Time Limit Exception
Example 1: Nested Loops
public static void processLargeDataset() {
List accounts = [SELECT Id, Name FROM Account LIMIT 5000];
List contacts = [SELECT Id, Name, AccountId FROM Contact];
for (Account acc : accounts) {
for (Contact con : contacts) {
if (con.AccountId == acc.Id) {
System.debug('Matching Contact: ' + con.Name);
}
}
}
}
- Cause: The nested loop processes all accounts and contacts, consuming significant CPU time.
- Fix: Use maps or bulk operations to optimize the logic.
Example 2: SOQL inside For Loops
public static void processContacts() {
List accounts = [SELECT Id FROM Account LIMIT 5000];
for (Account acc : accounts) {
List contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
System.debug('Contacts for Account: ' + contacts.size());
}
}
- Cause: A SOQL query inside the loop executes repeatedly for each account.
- Fix: Bulkify the query by fetching all data in a single query.
Example 3: Recursive Triggers
trigger AccountTrigger on Account (after update) {
for (Account acc : Trigger.new) {
acc.Name = acc.Name + ' Updated';
update acc; // Recursive trigger execution
}
}
- Cause: The trigger calls itself repeatedly, resulting in infinite recursion.
- Fix: Use static variables to prevent recursive execution
4. Techniques to Avoid CPU Time Limit Exception
There are 6 different ways, using which you can easily avoid CPU Time Limit Exception.
- Bulkify your Apex Code.
- Use Maps for Efficient Data Handling
- Avoid Recursion inside triggers
- Optimise your SOQL Queries
- Use Asynchronous ways to process large number of records
- Reduce System.Debug statements in your apex code.
Now, lets understand each of the above ways with an example.
a. Bulkify Your Code
Bulkification means – To handle multiple records in a single operation so that it will minimize the processing time.
For Example:
List accounts = [SELECT Id, Name FROM Account WHERE Name LIKE 'Test%'];
List contacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accounts];
Map> accountContactMap = new Map>();
for (Contact con : contacts) {
if (!accountContactMap. containsKey (con.AccountId)) {
accountContactMap.put (con.AccountId, new List());
}
accountContactMap.get (con.AccountId).add(con);
}
b. Use Maps for Efficient Data Handling
Using Maps can also help in avoiding CPU Time Limit Exception, as it provides quick lookups, reducing the need for nested loops.
For Example:
Map accountMap = new Map([SELECT Id, Name FROM Account]);
c. Reduce Recursive Trigger Calls
There are several effective methods to prevent infinite recursion in triggers, such as using a static boolean variable or a Set.
Check below guide to learn various techniques developers can use to stop recursion in triggers.
For Example:
public class TriggerHelper {
public static Boolean isFirstRun = true;
}
trigger AccountTrigger on Account (after update) {
if (TriggerHelper.isFirstRun) {
TriggerHelper.isFirstRun = false;
// Trigger logic
}
}
d. Optimize SOQL Queries
 Avoid querying unnecessary fields or running multiple queries when one is sufficient.
Bad Practice:
List accounts = [SELECT Id, Name, Industry FROM Account];
List contacts = [SELECT Id, Name, Email FROM Contact];
Good Practice:
List accountsWithContacts = [SELECT Id, Name, (SELECT Id, Name FROM Contacts) FROM Account];
e. Use Asynchronous Processing
 Move long-running operations to asynchronous methods like Batch Apex or Queueable Apex.
Example: Batch Apex for Large Data
global class ProcessLargeDataBatch implements Database.Batchable {
global Database. QueryLocator start(Database .BatchableContext context) {
return Database.getQueryLocator ('SELECT Id, Name FROM Account');
}
global void execute (Database.BatchableContext context, List scope) {
for (Account acc : (List)scope) {
acc.Name += ' Processed';
}
update scope;
}
global void finish (Database.BatchableContext context) {
System.debug('Batch processing complete.');
}
}
f. Reduce System.debug Statements
Remove or minimize the use of System.debug() in production code to avoid overhead.
5. Best Practices to Optimize Apex Code
1. Use Collections Effectively:
•Prefer Lists, Sets, and Maps for data handling.
•Use Maps for lookups instead of nested loops.
2.Avoid Hardcoding Limits:
•Dynamically handle data using pagination to avoid processing excessive records.
3.Batch Your Data:
•Use batch processing or pagination for large datasets.
4.Write Efficient Queries:
•Fetch only the fields you need.
•Avoid unnecessary relationships in SOQL.
5.Test for Performance:
•Use debug logs to measure CPU time and optimize slow sections of your code.
6. Real-World Example: Fixing CPU Time Limit Exception
Problem Code:
public static void inefficientProcess() {
List accounts = [SELECT Id, Name FROM Account LIMIT 5000];
for (Account acc : accounts) {
List contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
System.debug('Contacts: ' + contacts.size());
}
}
Optimized Code:
public static void optimizedProcess() {
List accounts = [SELECT Id, Name FROM Account LIMIT 5000];
List contacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accounts];
Map> accountContactMap = new Map>();
for (Contact con : contacts) {
if (!accountContactMap. containsKey (con.AccountId)) {
accountContactMap.put (con.AccountId, new List());
}
accountContactMap.get (con.AccountId).add(con);
}
for (Account acc : accounts) {
System.debug('Account ' + acc.Name + ' has ' + accountContactMap. get(acc.Id).size() + ' contacts.');
}
}
Outcome:
This optimized code reduces CPU time by eliminating redundant queries and using efficient data structures.
Apart from CPU Time Limit Exception, Salesforce developers may encounter various other exceptions while designing and implementing various Salesforce business requirements.
Explore the post below to learn about these different types of exceptions, complete with examples.
7. Conclusion
The CPU Time Limit Exception is a safeguard to ensure fair resource usage in Salesforce’s multi-tenant environment.
By identifying inefficiencies and following best practices like bulkification, using maps, and leveraging asynchronous Apex, you can write efficient and scalable Apex code.
Regularly test your code’s performance to ensure it stays within governor limits.