Tuesday, December 20, 2022

Salesforce Governor Limits? Best Practices & Examples

In this article, I will explain what governor limits in Salesforce actually are. I’ll also provide some examples of the different types, while highlighting why they are so important within a multitenancy set-up.

What is Salesforce Multitenancy?

Salesforce operates on a form of software multitenancy. In layman’s terms, this means that a single instance of software (in this case, Salesforce) runs on a server that is able to serve multiple users.

Due to the nature of multitenancy and the fact we are sharing resources, governor limits are in place to ensure that any Apex we develop does not monopolize the shared resources we are allocated.

Imagine this – there are 100 flats in a single apartment building. The 100 flats share resources: water, electricity, gas, and wi-fi bandwidth. Imagine if several flats began bitcoin mining which started to hog all of the electricity. Then imagine that some other flats started to download huge 4k video files from the internet. Also imagine what would happen if other flats were running baths all at the same time. If you lived in this apartment building, things would quickly turn into a nightmare. You wouldn’t be able to stream any videos online in any reasonable time frame, your electricity bills would quadruple in a single month, and the water pressure in your shower would decline to an embarrassing trickle.

The apartment building represents a Salesforce instance (also known as a pod or server). The flats are the individual Salesforce orgs (there can be tens of thousands of orgs on a single sever). The distribution of shared resources is the multi-tenancy architecture.

This analogy helps to explain why governor limits are so helpful – they prevent inconsiderate users from hogging the database, memory, network, or processor resources that you (and other customers on the same server) need to effectively run business processes in the cloud. Governor limits dictate what your code can and cannot do when it is executed on the Salesforce platform. And this contributes to the scalability of the platform, since the resource allocation is defined.

What Are Salesforce Governor Limits?

Simply put, Salesforce Governor Limits are usage caps enforced by Salesforce to ensure efficient processing. They allow for multiple users of the platform without impeding performance.

There are many different types of governor limits, some of which are tied to your Salesforce edition. For example, Professional Edition enables one to have 50 custom objects, with up to 2,000 for Unlimited and Performance.

Some other governor limits have soft limits that can either be solved by Salesforce or by purchasing additional add-ons. For example, the current limit is 6MB of Apex code per org, but this can be increased on a case-by-case basis by Salesforce Support. Other limits are based on a combination of both your Salesforce edition but also the number of user licenses on the org (For example, API and data storage limits).

Other governor limits are associated more with programming in Apex: for example, an Apex class / execute anon script can only have 100 SELECT statements per synchronous transaction. Some of these limits are hard – they cannot be increased, so a new approach (by the developer) would be needed to achieve the desired result.

This trailhead on multi-tenancy and this Salesforce Summary article go into more detail on this subject.

Types of Salesforce Governor Limits

  • Per-Transaction Apex Limits: These limits count for each Apex transaction. For Batch Apex, these limits are reset for each execution of a batch of records in the execute method.
  • Per Transaction Certified Managed Package Limits: If a managed package developed by a Salesforce ISV has passed security review, they are provided with generally higher per-transaction limits.
  • Lightning Platform Apex Limit: These limits aren’t specific to an Apex Transaction and are enforced by the Lightning platform.
  • Static Apex Limit: Apex Limits that are applied across all transactions.
  • Size-Specific Apex Limit: Apex Limits related to the size of code.
  • Miscellaneous Apex Limit: Other Limits!

Why Are Salesforce Governor Limits Important?

Governor Limits are a concept that all Salesforce Developers (and Salesforce Admins to a lesser extent) must grasp. They will fundamentally shape the way that you architect Salesforce solutions, as well as how the code is written.

If a Salesforce Governor Limit is hit, the associated governor issues a runtime exception that cannot be handled. In other words, your code breaks and won’t work.

Governor Limit Best Practices

As per the official docs, there are a huge number of different types of governor limits. This article will cover some of the more common ones associated with Apex programming, and outline approaches that can be taken to resolve them.

1. SOQL 100 Limit

One of the most well known governor limits is that an apex class / execute anonymous script can ‘only’ have 100 SELECT statements per apex transaction.

An apex transaction is a set of operations executed in a single unit. For example, when a trigger runs on every Account that has its name updated, processing is done to update all references across tasks and events from the old Account name to the new Account name. The sum of operations and resources leveraged that take place to achieve this is the “apex transaction”.

To demo this particular governor limit, go to a sandbox and navigate to the Developer Console. Either input cmd + E or Debug | Open Execute Anonymous Window and input the following:

for (integer i = 0; i <= 100; i++)
{
    Integer countOfAccounts = [SELECT COUNT() FROM Account];
}

Upon highlighting and then selecting “Execute Highlighted” in the Dev console, you’ll receive the following error:

This is happening as per the docs – you can only have 100 SELECT statements in a synchronous context.

 

SOQL 100 Limit Fix

Most commonly, this governor limit will be hit because there is a SOQL statement in a for loop, which is considered one of the ‘cardinal sins’ of programming in Apex.

List<Account> accounts = [SELECT Id, Name FROM Account];
List<Integer> contactCount = new List<Integer>();
for (Integer i = 0; i <= accounts.size(); i++)
{
   Integer count = [SELECT COUNT() FROM Contact WHERE AccountId IN : accounts ];
   contactCount.add(count);
}

The moment there are more than 99 accounts in the variable ‘accountList’, the code will fail.

A simple solution is simply to move all SOQL statements outside of for loops, and especially if the for loop iterator is dynamic (the number of accounts in the list could change but if the iterator is a number, then that is fixed). To do that, you’ll likely be depending more on other collections and inventive SOQLs.

An approach can be like so:

Map<Id, Account> accountMap = new Map<Id, Account>([SELECT Id FROM Account]);
Map<String, Integer> accountNamesAndRespectiveContactCounts = new Map<String, Integer>();
for(Account accountObject : [SELECT Name, (SELECT Id FROM Contacts) FROM Account WHERE Id IN :accountMap.keySet()]) 
{    
accountNamesAndRespectiveContactCounts.put(accountObject.Name,     accountObject.Contacts.size()); 
}

system.debug('accountObject map is: ' + accountNamesAndRespectiveContactCounts);

2. DML 150 Limit

Probably the second most well-known governor limit concerns the number of DML operations in a single transaction. As per the docs, the limit is 150:

To demonstrate this in a developer sandbox, you can run the following execute anonymous code:

List<Account> accountList = new List<Account>();
Account accountObject;
for (Integer i = 0; i < 150; i++)
{
    accountObject = new Account();
    accountObject.Name = 'Test ' + i;
    accountList.add(accountObject);
}
insert accountList;

This will create 150 Accounts. 

Now try to execute the following code:

Contact contactObject;
for (Account accountIterator : [SELECT Id, Name FROM Account])
{
    contactObject = new Contact();
    contactObject.AccountId = accountIterator.Id;
    contactObject.LastName = 'Surname ' + accountIterator.Name;
    insert contactObject;
}

Your execute anon should look like this:

You will hit the following error:

DML 150 Limit Fix

Again, it’s a fairly straightforward fix. You should not use DML statement inside for loops (by the way, this is something that good code scanning tools such as PMD will pick up on). Instead, you should leverage collections to store the data. Then, when you do a DML operation on a collection, it only counts as one DML!

To demonstrate, let’s execute the following code, and for good measure, let’s leverage some of the helpful methods from the Limits class.

List<Contact> contactList = new List<Contact>();
Contact contactObject;
for (Account accountIterator : [SELECT Id, Name FROM Account LIMIT 150])
{
     contactObject = new Contact();
     contactObject.AccountId = accountIterator.Id;
     contactObject.LastName = ‘Surname ‘ + accountIterator.Name;
     contactList.add(contactObject);
}
If (contactList.size() > 0)
{ 
    Insert contactList;
}
system.debug(‘Size of contact list is: ‘ + contactList.size());
system.debug(‘Count of DML statements: ‘ + Integer.valueOf(Limits.getDmlStatements()));

Opening up the debug log will display the following:

That is very cool – you could have a collection with tens of thousands of records (or much more) and yet still only use one of your 150 available DML statements. Talk about efficient!

Governor Limits Practice Exercises

Practice makes perfect, so let’s do some exercises together. Good luck!

Question 1

How would you refactor this? Find the answer here.

Task taskObject;
for (Contact contactObject : [SELECT Id, AccountId, LastName FROM Contact])
{
    taskObject = new Task(WhoId = contactObject.Id);
    insert taskObject;
}

Question 2

How would you refactor this? Find the answer here.

for (Account accountObject : [SELECT Id, Description FROM Account])
{
    Integer countOfEvents = [SELECT COUNT() FROM EVENT WHERE WhatId =: accountObject.Id];
    if (countOfEvents > 0)
    {
        accountObject.Description = String.valueOf(countOfEvents);
    }
    update accountObject;
}

The above answer is a way to achieve the outcome. It leverages AggregateResult and different collections to update the Description field on the Account object with the value of the number of event records that the account has.

Question 3

How would you refactor this? Find the answer here.

Are you stuck? See ‘SOQL 100 Limit Fix’ to jog your memory!

List<Account> accounts = [SELECT Id, Name FROM Account];
List<Integer> contactCount = new List<Integer>();
for (Integer i = 0; i <= accounts.size(); i++)
{
    Integer count = [SELECT COUNT() FROM Contact WHERE AccountId IN : accounts ];
    contactCount.add(count);
}

Question 4

How would you refactor this? Find the answer here.

Are you stuck? See ‘DML 150 Limit Fix’ to jog your memory!

List<Account> accountList = new List<Account>();
Account accountObject;
for (Integer i = 0; i < 150; i++)
{
    accountObject = new Account();
    accountObject.Name = 'Test ' + i;
    accountList.add(accountObject);
}
insert accountList;

Contact contactObject;
for (Account accountIterator : [SELECT Id, Name FROM Account])
{
    contactObject = new Contact();
    contactObject.AccountId = accountIterator.Id;
    contactObject.LastName = 'Surname ' + accountIterator.Name;
    insert contactObject;
}

Summary

In conclusion, there are many different types of governor limits across Salesforce and for good reason – to help us become more efficient developers and admins. There are some general patterns you can adopt to ensure that you stay below the limits. Generally speaking, the most well-known limits are those around SOQL and DML limits in a single transaction. Hopefully you have also learned that getting past governor limits can be challenging and fun at the same time.

No comments:

Post a Comment

Understanding Wire vs Imperative Apex Method Calls in Salesforce Lightning Web Components (LWC)

Understanding Wire vs Imperative Apex Method Calls in Salesforce Lightning Web Components (LWC) Introduction: Salesforce Lightning Web ...