How to Increase Salesforce Governor Limits


Salesforce is known as CRM with a lot of Limits. Because Salesforce Apex runs in a multitenant environment, the Apex runtime engine strictly enforces limits so that runaway Apex code or processes don’t monopolize shared resources. If some Apex code exceeds a limit, the associated governor issues a runtime exception that cannot be handled.

But often Salesforce developers faced with cases when they need to perform more:

  • SOQL queries
  • DML operations
  • Http Callouts

in one synchronous execution transaction. Salesforce gives an ability to perform more operations but it will be run asynchronously.

Solution

The solution is to split one execution transaction in several by using Visualforce page and PageReference.getContent() method.

Visualforce page can have an action that will run on page load and also we can pass different GET parameters to the page.

<apex:page controller="GS_RestApiController" 
           action="{!execute}" 
           contenttype="text/javascript" 
           showHeader="false" 
           sidebar="false">
    <apex:outputText value="{!response}" escape="false"/>
</apex:page>

We already used this page in the past article How To Build Custom Flexible Rest Architecture In The Salesforce. You can find more information there.

We can run different code based on request parameters by using Type.forName() and Type.newInstance() methods. It gives us the ability to create a new instance of any classes dynamically. More info you can find in GS_CommandFactory class.

Typically Command looks like a class that extends base GS_Command class and overrides perform() and getMessage() methods.

public class GS_100QueryTestCommand extends GS_Command {

    public override Object perform() {
        for (Integer i = 0; i < Limits.getLimitQueries(); i++) {
            List<Account> accountList = [
                SELECT Id, AccountNumber, AccountSource
                FROM Account
            ];
        }
        return new Map<String, Integer>{
            'getLimitQueries' => Limits.getLimitQueries(),
            'getQueries' => Limits.getQueries()
        };
    }

    public override String getMessage() {
        return '100 SOQL queries';
    }
}

To run different commands we use GS_Executor class. It used PageReference.getContent() method to run action in the Visulaforce page.

public class GS_Executor {

    public static Object execute(String commandName, Map<String, String> commandParam, Type returnType) {
        try {
            PageReference restApiPage = Page.GS_RestApi;
            restApiPage.getParameters()
                .put(GS_RestApiController.COMMAND_NAME_PARAM, commandName);
            if (commandParam != null) {
                for (String paramName : commandParam.keySet()) {
                    restApiPage.getParameters()
                        .put(paramName, commandParam.get(paramName));
                }
            }
            Blob blobResult = restApiPage.getContent();
            String stringResult = blobResult.toString();
            GS_RestResponse response = (GS_RestResponse)
                JSON.deserialize(stringResult, GS_RestResponse.class);
            if (response.code == GS_StatusCode.OK) {
                if (returnType != null) {
                    return JSON.deserialize(response.result, returnType);
                } else {
                    return response.result;
                }
            } else if (response.code == GS_StatusCode.ERROR) {
                throw new GS_Exception(response.message);
            }
            return null;
        } catch (GS_Exception gsException) {
            throw gsException;
        } catch (Exception exp) {
            throw new GS_Exception(exp.getMessage());
        }
    }
}

Use case

The framework will be usefull if you need to perform more

  • SOQL queries
  • DML operations
  • Http Callouts

For example, I used 100QueryTest command that performs 100 queries to the Account object.

public class GS_SalesforceLimitTest {

    public static void testSoqlLimit() {
        System.debug('Limits.getCallouts() = ' + Limits.getCallouts());
        System.debug('Limits.getHeapSize() = ' + Limits.getHeapSize());
        System.debug('Limits.getCpuTime() = ' + Limits.getCpuTime());

        Integer totalQueriesNumber = 0;
        for (Integer i = 0; i < 50; i++) {
            Map<String, Integer> result = (Map<String, Integer>) 
                GS_Executor.execute('100QueryTest', null, 
                Map<String, Integer>.class);
            totalQueriesNumber += result.get('getQueries');
        }
        System.assertEquals(5000, totalQueriesNumber, 
            'Total Queries Number Mismatch');

        System.debug('Limits.getCallouts() = ' + Limits.getCallouts());
        System.debug('Limits.getLimitCallouts() = ' + 
            Limits.getLimitCallouts());

        System.debug('Limits.getHeapSize() = ' + Limits.getHeapSize());
        System.debug('Limits.getLimitHeapSize() = ' + 
            Limits.getLimitHeapSize());

        System.debug('Limits.getCpuTime() = ' + Limits.getCpuTime());
        System.debug('Limits.getLimitCpuTime() = ' + 
            Limits.getLimitCpuTime());
    }
}

Totally this code will run 5000 queries, that is 50 time more than limitation.

Output of the class above will looks like:

// Before call to GS_RestApi.page
USER_DEBUG [4]|DEBUG|Limits.getCallouts() = 0
USER_DEBUG [5]|DEBUG|Limits.getHeapSize() = 1091
USER_DEBUG [6]|DEBUG|Limits.getCpuTime() = 3

// After call to GS_RestApi.page
USER_DEBUG [15]|DEBUG|Limits.getCallouts() = 0
USER_DEBUG [16]|DEBUG|Limits.getLimitCallouts() = 100

USER_DEBUG [18]|DEBUG|Limits.getHeapSize() = 1475
USER_DEBUG [19]|DEBUG|Limits.getLimitHeapSize() = 6000000

USER_DEBUG [21]|DEBUG|Limits.getCpuTime() = 9853
USER_DEBUG [22]|DEBUG|Limits.getLimitCpuTime() = 10000

Based on it we can make a few conclusions:

  • CPU time is shared between all call because we need time to perform queries
  • Heap Size will increase if you need to return a lot of data from every call to the page
  • PageReference.getContent() isn’t counted as Callout

Framework source code you can find on my GitHub – https://github.com/SergeyTrusov/increase-salesforce-limits