Salesforce Governor Limits: Issues and Fixes

Estimated reading time: 7 minutes

Salesforce Governor Limits: Issues and Fixes

operates in a multi-tenant environment, where resources are shared across multiple organizations. To ensure fair usage and prevent any single process from monopolizing these resources, Salesforce enforces strict limits on execution. These are known as Governor Limits. Exceeding these limits results in runtime exceptions that cannot be handled, causing your code to fail.

Common Governor Limits and How to Address Them

1. SOQL Query Limit (100 per synchronous transaction, 200 per asynchronous transaction)

Issue: SOQL Inside Loops

Performing SOQL queries inside a loop is a common cause of hitting the SOQL query limit. The query executes for each iteration of the loop, quickly consuming the allowed limit.

// Problematic Code
List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 10];
for (Account acc : accounts) {
    List<Contact> contacts = [SELECT Id, FirstName, LastName FROM Contact WHERE AccountId = :acc.Id];
    // Process contacts
    for (Contact con : contacts) {
        System.debug(con.FirstName + ' ' + con.LastName);
    }
}

Fix: Bulkify SOQL Queries

Retrieve all necessary data in a single SOQL query before the loop using a collection to filter.

// Corrected Code
List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 10];
Set<Id> accountIds = new Set<Id>();
for (Account acc : accounts) {
    accountIds.add(acc.Id);
}
List<Contact> allContacts = [SELECT Id, FirstName, LastName, AccountId FROM Contact WHERE AccountId IN :accountIds];

// Process contacts using a Map for efficient lookup
Map<Id, List<Contact>> accountContactMap = new Map<Id, List<Contact>>();
for (Contact con : allContacts) {
    if (!accountContactMap.containsKey(con.AccountId)) {
        accountContactMap.put(con.AccountId, new List<Contact>());
    }
    accountContactMap.get(con.AccountId).add(con);
}

for (Account acc : accounts) {
    if (accountContactMap.containsKey(acc.Id)) {
        for (Contact con : accountContactMap.get(acc.Id)) {
            System.debug(con.FirstName + ' ' + con.LastName);
        }
    }
}

2. DML Statement Limit (150 per transaction)

Issue: DML Inside Loops

Performing Data Manipulation Language (DML) operations (insert, update, delete, upsert, undelete) inside a loop will quickly exceed the DML statement limit.

// Problematic Code
List<Account> accountsToUpdate = new List<Account>();
for (Integer i = 0; i < 200; i++) {
    Account acc = new Account(Name = 'Temporary Account ' + i, Description = 'Initial');
    insert acc; // DML inside loop - will hit limit
    acc.Description = 'Updated';
    update acc; // Another DML inside loop
    accountsToUpdate.add(acc);
}
// Further processing of accountsToUpdate

Fix: Bulkify DML Operations

Collect all the records you want to operate on in a list and perform the DML operation on the entire list at once.

// Corrected Code
List<Account> accountsToInsert = new List<Account>();
List<Account> accountsToUpdate = new List<Account>();
for (Integer i = 0; i < 200; i++) {
    Account acc = new Account(Name = 'Temporary Account ' + i, Description = 'Initial');
    accountsToInsert.add(acc);
    Account accToUpdate = new Account(Name = 'Temporary Account ' + i, Description = 'Updated');
    accountsToUpdate.add(accToUpdate);
}
insert accountsToInsert; // Bulk DML
update accountsToUpdate; // Bulk DML
// Further processing of accountsToUpdate

3. Time Limit Exceeded (10,000 milliseconds per synchronous transaction)

Issue: Inefficient Code and Complex Logic

Complex , deeply nested loops, and inefficient SOQL queries can consume excessive CPU time, leading to this limit being exceeded.

// Potentially Problematic Code (Illustrative - complexity can vary)
List<Account> accounts = [SELECT Id, Name, Industry, AnnualRevenue FROM Account WHERE NumberOfEmployees > 1000];
for (Account acc : accounts) {
    for (Integer i = 0; i < 1000; i++) {
        // Perform some complex calculations based on acc.Industry and acc.AnnualRevenue
        Decimal result = Math.sqrt(acc.AnnualRevenue / (i + 1));
        // ... more complex operations ...
    }
    // Update the account based on the calculations
    acc.Description = String.valueOf(result);
    update acc; // DML inside loop (also a DML issue)
}

Fix: Optimize Code and Algorithms

  • Review and simplify complex logic.
  • Avoid deeply nested loops where possible.
  • Optimize SOQL queries by using appropriate filters and indexes.
  • Consider using asynchronous processing (e.g., Batch , Queueable Apex) for long-running operations.
  • Minimize the amount of data processed in a single transaction.
// Improved Code (Illustrative)
List<Account> accountsToUpdate = new List<Account>();
List<Account> accounts = [SELECT Id, Name, Industry, AnnualRevenue FROM Account WHERE NumberOfEmployees > 1000 LIMIT 200]; // Limit the scope

for (Account acc : accounts) {
    Decimal result = calculateComplexResult(acc.Industry, acc.AnnualRevenue); // Move complex logic to a helper method
    acc.Description = String.valueOf(result);
    accountsToUpdate.add(acc);
}

if (!accountsToUpdate.isEmpty()) {
    update accountsToUpdate; // Bulk DML
}

// Helper method for complex calculation
private Decimal calculateComplexResult(String industry, Decimal revenue) {
    Decimal result = 0;
    // Optimized calculation logic
    for (Integer i = 0; i < 100; i++) { // Reduced iterations
        result += Math.log(revenue + i + 1);
    }
    return result;
}

4. Total Heap Size Limit (6 MB for synchronous, 12 MB for asynchronous)

Issue: Holding Large Amounts of Data in Memory

Creating and manipulating very large collections (Lists, Sets, Maps) or complex objects can lead to exceeding the heap size limit.

// Problematic Code
List<List<SObject>> massiveData = new List<List<SObject>>();
for (Integer i = 0; i < 10000; i++) {
    List<Account> tempAccounts = [SELECT Id, Name FROM Account LIMIT 10];
    massiveData.add(tempAccounts);
    // ... more data added to massiveData ...
}
// Processing massiveData - likely to exceed heap limit

Fix: Process Data in Batches and Avoid Unnecessary Data Storage

  • Process data in smaller chunks or batches.
  • Avoid storing large amounts of data in memory if it can be processed sequentially.
  • Use techniques like .QueryLocator for processing large query results in chunks.
  • Be mindful of the size of objects and collections you create.
// Corrected Code using Database.QueryLocator
Database.QueryLocator ql = Database.getQueryLocator([SELECT Id, Name FROM Account]);
Database.executeBatch(new AccountBatchProcessor(), 200); // Process accounts in batches of 200

public class AccountBatchProcessor implements Database.Batchable<sObject> {
    public Database.QueryLocator start(Database.BatchableContext bc) {
        return ql;
    }

    public void execute(Database.BatchableContext bc, List<Account> scope) {
        // Process the current batch of accounts (scope)
        for (Account acc : scope) {
            System.debug('Processing Account: ' + acc.Name);
            // Perform operations on the batch
        }
    }

    public void finish(Database.BatchableContext bc) {
        // Optional finish logic
    }
}

General Best Practices to Avoid Governor Limits

  • Bulkify Your Code: Always your code to handle multiple records efficiently, especially for SOQL and DML operations.
  • Avoid SOQL and DML Inside Loops: This is a primary cause of exceeding limits. Move these operations outside loops.
  • Use Collections Effectively: Leverage Lists, Sets, and Maps to process data efficiently in bulk.
  • Write Efficient Queries: Use appropriate filters (WHERE clause) and indexes to minimize the number of records processed by your SOQL queries.
  • Limit the Scope: Only query and process the data you absolutely need. Use LIMIT clauses when appropriate.
  • Use Asynchronous Processing: For long-running or resource-intensive operations, consider using Batch Apex, Queueable Apex, or Future Methods to execute them outside the immediate transaction with higher limits.
  • Be Mindful of Recursion: Recursive triggers or chained method calls can quickly consume CPU time and other limits. Implement logic to prevent infinite recursion.
  • Test Thoroughly: Write comprehensive unit tests that simulate processing large volumes of data to identify potential governor limit issues early in the development cycle.
  • Use Database.QueryLocator for Large Datasets: For querying a large number of records, Database.QueryLocator allows you to process them in manageable chunks within Batch Apex.
  • Consider the Order of Operations: Be aware of the Salesforce order of execution and how different events and processes interact, as this can impact limit consumption.
  • Use the Limits Apex Methods: The Limits class provides methods to check the current consumption of various governor limits within your code, allowing for proactive management.

By understanding and adhering to these best practices, you can write robust and scalable Apex code that operates within Salesforce’s governor limits, ensuring the stability and of your applications.

Agentic AI (26) AI Agent (22) airflow (4) Algorithm (34) Algorithms (27) apache (40) apex (11) API (106) Automation (25) Autonomous (26) auto scaling (3) AWS (40) aws bedrock (1) Azure (29) BigQuery (18) bigtable (3) blockchain (3) Career (5) Chatbot (17) cloud (79) code (28) cosmosdb (1) cpu (26) Cybersecurity (5) database (88) Databricks (14) Data structure (11) Design (74) dynamodb (4) ELK (1) embeddings (10) emr (4) examples (11) flink (10) gcp (18) Generative AI (10) gpu (10) graph (19) graph database (1) graphql (1) image (18) index (16) indexing (11) interview (7) java (36) json (58) Kafka (26) LLM (29) LLMs (9) Mcp (1) monitoring (68) Monolith (8) mulesoft (8) N8n (9) Networking (11) NLU (2) node.js (10) Nodejs (6) nosql (14) Optimization (41) performance (79) Platform (72) Platforms (46) postgres (19) productivity (9) programming (23) pseudo code (1) python (59) RAG (126) rasa (3) rdbms (2) ReactJS (1) realtime (1) redis (12) Restful (4) rust (10) salesforce (22) Spark (29) sql (49) time series (8) tips (2) tricks (14) use cases (62) vector (16) Vertex AI (15) Workflow (49)

Leave a Reply

Your email address will not be published. Required fields are marked *