Understanding the Bulk of the Salesforce1 Platform

In this blog post we discuss one of the basic concepts of the Salesforce1 platform: its ability to operate in Bulk, or in other words, its ability to process more than one record at a time. We touch on areas where bulk processing is relevant and provide examples for how to properly write Apex code for bulk processing.

Understanding the Bulk of the Salesforce1 Platform

At first sight a lot of programming concepts and constructs of the Salesforce1 cloud platform appear familiar to developers with a traditional programming background (Java, .NET) who are getting started on the Salesforce1 cloud platform. It’s of course comforting and easy to assume that these familiar concepts and constructs will also work in the same familiar way, but that’s not always the case. If “Too many SOQL queries: 101” or “Too many DML statements: 151” sounds familiar to you, chances are you have not properly bulkified your Apex code.

In this blog post we will discuss one of the basic concepts of the Salesforce1 platform: its ability to operate in Bulk, or in other words, its ability to process more than one record at a time. We will touch on areas where bulk processing is relevant and provide examples for how to properly write Apex code for bulk processing.

What does Bulk in the context of Salesforce1 mean?

It really means that certain areas of the Salesforce1 platform are optimized to process multiple records at a time. The most common areas where this concept applies are:

  • Data Loading — When loading data into Salesforce via the API or tools like the Data Loader or Workbench, records are processed in batches of up to 200 records at a time, rather than a single record at a time.
  • Triggers — Similar to traditional database systems, Salesforce1 has the concept of triggers on objects. However, instead of operating on a single record at time, triggers also operate in bulk and process record batches of up to 200 records. For example, if you’re loading data into Salesforce1 via the API and insert 1000 records into an object, only 5 API calls, with batches of 200 record each, are being made to insert the data. If a trigger is defined on the object that fires before insert, the trigger will also execute only 5 times, passing a collection of 200 records into each trigger invocation.
  • DML statements in Apex code — Similar to the bulk concept in data loading, data manipulation (inserting, updating, deleting and undeleting) of records in Apex code can be done in bulk as well, meaning that records can be organized in collections and then processed in a single DML statement, rather than executing a separate DML statement for every single record that needs to be processed.

Governor limits

Another important concept of the Salesforce1 platform to understand, especially but not only in the context of bulkifying your Apex code, is Governor Limits. Because the Salesforce1 platform is a multitenant environment, the runtime engine for Apex code strictly enforces a number of limits to ensure that runaway Apex code doesn’t monopolize shared resources. If any code ever exceeds a limit, the associated governor issues a runtime exception. A full list of all Apex Governor Limits can be found here and I strongly recommend familiarizing yourself with these limits and how the governors work. In the context of this blog post, we’re only going to focus on two governor limits:

  • Total number of SOQL queries issued per transaction: 100
  • Total number of DML statements issued per transaction: 150

How to bulkify your code and avoid exceeding governor limits

Bulkifying Triggers

Let’s take a look at triggers first. As mentioned above, triggers aren’t designed to operate on a single record at a time but instead can process up to 200 records in a single invocation. Let’s look at some code examples to better understand the concept and how to properly prepare our trigger code for this.

When a trigger is invoked before or after a DML event, the records of the DML event are passed into the trigger as a collection of records or so-called list. In case of an insert operation, all records will be passed into the trigger in the Trigger.new list.

The following code sample is an example of a flawed programming pattern. While it won’t cause the trigger to exceed any governor limits or result in any runtime exception when the trigger executes, it assumes that only one record is passed into the trigger as it operates only on the first element of the Trigger.new list. This might support most user interface events, but it does not support bulk operations invoked through the API (e.g. data loads), Visualforce, or any operation a user might perform on multiple records from an Enhanced Listview in the user interface.

trigger AddAcctOwnerEmployeeId on Account (before insert, before update) {
   Account a = Trigger.new[0];
   a.Owner_EmpId__c = [SELECT EmpId__c from User WHERE Id = :a.OwnerId].EmpId__c;
}

Here is another code example. While this code is better in the sense that it is properly written to operate on all records in the Trigger.new list, it is still flawed as it doesn’t consider the Governor Limit of the total number of SOQL queries issued per transaction, which is 100. The SOQL statement is placed inside the for loop that loops over the elements of the Trigger.new list. If there are more than 100 elements in the list, the trigger will exceed the SOQL query limit and throw a “Too many SOQL queries: 101” runtime exception.

trigger AddAcctOwnerEmployeeId on Account (before insert, before update) {
   for (Account a : Trigger.new){
     a.Owner_EmpId__c = [SELECT EmpId__c from User WHERE Id = :a.OwnerId].EmpId__c;
   }
}

Understanding Sets and Maps

When working on bulkifying Apex code, there are two programming constructs that are important to understand: Sets and Maps.

Sets are unordered collections of distinct elements. The elements can be of any data type – primitive types, collections, Salesforce Objects, etc. For example, the following represents a set of strings, that uses city names:

“San Francisco” “New York” “Paris” “Tokyo”

Maps are collections of key-value pairs where each unique key maps to a single value. Keys and values can be of any data type. The following represents a map of countries and currencies:

Country (Key) “United States” “Japan” “France” “England” “India”
Currency (Value) “Dollar” “Yen” “Euro” “Pound” “Rupee”

Now let’s look at the properly bulkified trigger code using Sets and Maps. It demonstrates the correct pattern to support the bulk nature of triggers while respecting the governor limits by creating a Set of distinct Account OwnerId’s for all account records in the Trigger.new list and then using the Set in a single SOQL statement outside of any loops to create a Map of Account OwnerId and their associated Employee Id. In the for loop that processes all the records in the trigger.new list the Map is then used to set the Employee Id for all Account records that are processed by the trigger, without the need to make any SOQL statements inside the loop itself.

trigger AddAcctOwnerEmployeeId on Account (before insert, before update) {

  // For every account record, add the OwnerId to a set
  Set ownerIds = new Set();
  for (Account a : Trigger.new)
    ownerIds.add(a.OwnerId);   

  // Query the User object for the associated employee Ids and place the results
  // in a map
  Map owners = new Map([Select EmpId__c from User Where Id in :ownerIds]);

  // Now use the map to set the appropriate employee Id on every Account record
  // processed by the trigger.
  for (Account a : Trigger.new)
    a.EmpId__c = owners.get(a.OwnerId).EmpId__c;
}

Bulkifying DML Statements

Another important area for bulkification is when executing DML statements in Apex code. As in the example in the Bulkifying Triggers section above, here is a code example of Apex code that is not properly bulkified.

for (Account a: [SELECT id FROM Account WHERE BillingCountry = 'USA']){

   List contacts = [SELECT id, salutation, firstname, lastname, email FROM Contact where accountId = :a.Id];	  

   for(Contact c: contacts){
      c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;
      update c;
   }    	  
}

A few things will potentially cause problems here.

  1. The SOQL query to retrieve the Contact records is placed within a for loop. If the query for Account records that the loop is defined on returns more than 99 Account records, the SOQL query limit will be exceeded and this Apex code would throw a runtime exception.
  2. The DML statement update c to update the Contact records is also placed within a for loop and updates one Contact record at a time. If the Contact query returns more than 150 records for an Account, the DML statements per transaction limit would be exceeded.
  3. Finding the Account and associated Contact records in two separate SOQL queries is inefficient and could be done in a single SOQL query.

Here is the properly bulkified Apex code that achieves the same functionality while making sure that no Governor limit will be exceeded:

// This queries all Contacts related to the Account records in a single SOQL query.
// This is also an example of how to use child relationships in SOQL
List accountsWithContacts = [select id, name, (select id, salutation, description, firstname, lastname, email from Contacts) from Account WHERE BillingCountry = 'USA'];

// Create a list of Contact records that need to be updated 
List contactsToUpdate = new List{};

// for loop to iterate through all the queried Account records 
for(Account a: accountsWithContacts){
   // Use the child relationships dot syntax to access the related Contacts
   for(Contact c: a.Contacts){
      c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;
      // Add the Contact record to update to the list of Contact records to update 
      contactsToUpdate.add(c);
   }        
}

// Now perform a single Update DML statement for all contactsToUpdate outside the for Loop. 
update contactsToUpdate;

This code example illustrates the use of SOQL child relationships to collapse retrieving Account and associated Contact records into a single SOQL query. It holds the query results in the accountsWithContacts list and and the for loop then loops over the elements of that list.

Using child relationships in the query and the child relationship dot syntax in the loop eliminates the need for any additional SOQL statements within the loop. And instead of updating a single Contact record at a time within the for loop, Contact records that need to be updated are added to the contactsToUpdate list inside the loop and then processed in bulk in a single DML statement outside of the loop. No matter how many Account and Contact records will be returned by the SOQL query, this piece of code will only execute a single SOQL query and a single update DML statement.

Summary

The Salesforce1 platform is designed to process data in bulk, or multiple records at a time. In order for Apex code to function properly and respect the Governor limits, it needs to be written to operate in bulk as well. The most common and most important areas where proper bulkification of Apex code is relevant are Triggers, SOQL queries and/or DML statements. When developing your Apex code, think about bulkification and keep the following best practices in mind:

  • Design Apex code in triggers to process all records passed into the trigger.
  • Avoid executing SOQL queries or DML statements in loops.
    • Minimize the number of SOQL statements by pre-processing records and generating sets, which can be placed in single SOQL statement used with the IN clause.
    • Minimize the number of data manipulation language (DML) operations by adding records to collections and performing DML operations against these collections.

Related Resources

About the Author

Markus Spohn is an Architect Evangelist within the Technical Enablement team of the salesforce.com Customer-Centric Engineering group. The team’s mission is to help customers understand how to implement technically sound salesforce.com solutions. Check out all of the resources that this team maintains on the Architect Core Resources page of Developer Force.

Published
August 18, 2014

Leave your comments...

Understanding the Bulk of the Salesforce1 Platform