Given a scenario, identify the implications of governor limits on Apex transactions.
These are technical notes I compiled while studying using Focus on Force, a company that provides Salesforce training and certification prep services.
After studying this topic, you should be able to:
- Determine what governor limits and Apex transactions are
- Describe the implications of governor limits on Apex transactions
Table of Contents
- Governor Limits
- Best Practices & Considerations
- Scenarios and Solutions
Introduction
- Salesforce enforces governor limits to ensure resources are shared in the multi-tenant environment - these limits impact developers' coding practices:
- DML Statements: DML statements in an Apex transaction are rolled back if a governor limit is exceeded
- Query Inside Loop: A SOQL query inside a for loop can result in exceeding the governor limit
- Batch Apex: Batch Apex processes smaller batches of records to avoid exceeding governor limits
- Count() Function: Using the
count()
function in a SOQL query counts as one query row towards the limit - Limits Methods:
limits
methods allow returning the specific limit for a particular governor
Governor Limits
- Governor Limits ensure that one customer does not monopolize shared resources in a multi-tenant environment
- Example Limits: CPU time, memory used, how long a query can run, how many records are returned from a query
- Performance: Limits are monitored per transaction and per customer over a defined time period to ensure that performance in one org is not impacted bby another
- Transactions are also know as execution contexts
- Transactions are defined as a set of operations that is executed and evaluated as a single unit
- Each event that occurs in a single transaction is bound by their associated governor limits
- Can be initiated from various sources in the Salesforce platform:
- Apex triggers, class methods, anonymous code, web service, Visualforce page, custom Lightning component, process, Flow, etc
- Transactions are defined as a set of operations that is executed and evaluated as a single unit
- Exceeding Governor Limits resuls in:
- Terminated: current transaction is immediately terminated and unrecoverable
- Limit Excelption: the
System.LimitException
is thrown - this exception cannot be handled - Roll Back: entire transaction is rolled back and no data is committed to the database
- Limit on SOQL Queries: 100 queries per synchronous transaction, 200 queries per asynchronous transaction
- 101st query in a synchronous transaction causes the limit exception to be thrown
// The following throws a System.LimitException
List<Account> result;
for(Integer x=0; x< 101; x++>) {
try{
result = [SELECT Name FROM Account LIMIT 10];
System.debug('Queries: ' + Limits.getQueries());
System.debug('Query rows: ' + Limits.getQueryRows());
}catch(Exception ex){
System.debug('Caught Exception');
System.debug(ex);
}
}
- Limit on DML Statements: 150 DML statements of ANY kind per transaction (insert, update, upsert, delete, undelete, merge, emptyRecycleBin)
// The following throws a System.LimitException
for (Integer x=0; x<200; x++>) {
Account newAccount = new Account(Name='MyAccount-' + x);
try {
insert newAccount;
System.debug(Limits.getDMLStatements());
} catch (Exception ex) {
System.debug('Caught Exception');
System.debug(ex);
}
}
insert new Account(Name='MyAccount-last');
- Limit on Total Stack Depth: limit enforced on the total stack depth in a transaction that recursively fires triggers
- Ex: trigger invokes another trigger that then invokes the trigger that invoked it, creating a recursive loop
- Max depth of 16 for recursive Apex triggers
trigger AccountTrigger on Account (after update) {
for (Account a : Trigger.new) {
List<Contact contacts> = [SELECT Id, Description
FROM Contact
WHERE AccountId = :a.Id];
for (Contact c : contacts) {
c.Description = Test;
update c;
}
}
}
- Limit on Total Heap Size: heap size is the amount of memory allocated to aan object during a transaction
- Synchronous heap size limit: 6 MB
- Asynchronous heap size limit: 12 MB
- Ex: a list collection is used for storing the results of a SOQL query - memory allocated for the variable grows exponentially as more and more records are added to the collection.
Best Practices & Considerations
- Limits Apex Class: used to retrieve information for resources that have associated governor limits. Most common methods of the class:
getQueries()
/getLimitQueries()
: number of SOQL Queries issued / allowedgetQueryRows()
/getLimitQueryRows()
: number of records returned / allowed by SOQL queriesgetDMLStatements()
/getLimitDMLStatements()
: number of DML statements issued / allowed, such as insert, update, etcgetDMLRows()
/getLimitDMLRows()
: number of records processed / alowed by any DML statementgetHeapSize()
/getLimitHeapSize()
: approximate memory used / allowed for the heap
- Best practices & considerations:
- Database Rollback: DML operations will be rolled back when limits area exceeded
- Avoid SOQL inside loops
- Run within the Hea Size Limit: Use SOQL for-loops to process records in batches, especially when handling large data sets
- Use transient variables for Visualforce pages
- Remove items in a collection after use
- Check the
Limits.getHeapSize()
andLimits.getLimitHeapSize()
methods
- Count Function: SOQL queries that use the
COUNT()
orCOUNT(fieldname)
function to return an integer only counts as one query row toward the governor limit - SOQL in Apex Trigger: ex: SOQL inside a
trigger.new
loop may exceed the max SOQL queries allowed - Large number of records: use Batch Apex to break the record set down to smaller batches so governor limits are not reached
- DML Rows and Limits: use
Limits.getDMLRows()
aandLimits.getDMLRows()
// Use Limits methods as shown below to determine whether code should
// continue or abort an operation
System.debug('SOQL queries performed: ' + Limits.getQueries() +
' of ' + Limits.getLimitQueries());
System.debug('Number of records queried: ' + Limits.getQueryRows() +
' of ' + Limits.getLimitQueryRows());
System.debug('DML statements issued: ' + Limits.getDmlStatements() +
' of ' + Limits.getLimitDmlStatements());
if (Limits.getQueries() >= Limits.getLimitQueries()) {
System.debug('Performing another SOQL query will exceed the governor limits.');
} else {
List<Opportunity> records = [SELECT Id, Name, CloseDate, StageName
FROM Opportunity
WHERE AccountId IN :accountIds];
if ((records.size() + Limits.getDMLRows()) > Limits.getLimitDMLRows()) {
System.debug('Updating another record will exceed the governor limits.');
} elseif (Limits.getDmlStatements() >= Limits.getLimitDmlStatements()) {
System.debug('Performing another DML statement will exceed the governor limits.');
} else {
System.debug('You may proceed updating the records...');
}
}
- Example below shows how a SOQL for-loop and a list for-loop can be used to avoid hitting governor limits
- List iteration helps prevent exceeding the heap size limit when handling large data volumes
- Note version 3, below, will fail if there are more than 10,000 records - if so, use Limit methods to prevent failures
// In this example, there are 10,000 Lead records to process.
// Version 3 represents the recommended solution.
// Version 1: DML statements limit of 150 is easily exceeded
for (Lead ulead : [SELECT Id FROM Lead]) {
// modify lead...
update ulead;
}
// Version 2: DML rows limit of 10,000 is exceeded (or synchronous
// heap size limit of 6MB as list grows and other fields were included)
List<Lead> uleads = new List<Lead>();
for (Lead l : [SELECT Id FROM Lead]) {
// modify lead...
uleads.add(l);
}
update uleads;
// Version 3: Total DML statements issued is 50 only since a SOQL
// for-loop automatically processes records in batches of 200
for (List<Lead> leads : [SELECT Id FROM Lead]) {
List<Lead> uleads = new List<Lead>();
for (Lead l : leads) {
// modify lead...
uleads.add(l);
}
update uleads;
}
Scenarios and Solutions
Reference FoF Slides