Subscribe

Appirio RSS Feed
Subscribe with Bloglines

Add to Google
Subscribe in NewsGator Online

Community

Appirio Technology Blog

Tuesday, October 21, 2008

Using Client-Side Looping to Work within Salesforce.com Governor Limits

Chris Bruzzi

Repeat after me. The governor is our friend. It stops us from doing things we really shouldn't be doing, so in a way the governor makes us a better person. At least as far as SaaS development goes.

As you may already be too familar, Salesforce.com imposes limitations to ensure that their customers do not monopolize resources since they share a multi-tenant environment. These limits are called governors and are detailed in the Understanding Execution Governors and Limits section of the Apex Language Reference. If a script exceeds one of these limits, the associated governor issues a runtime exception and code execution is halted.
I am about to guide you through a simple example of using client-side looping in VisualForce to execute server-side Apex code that would otherwise have been unacceptable based on the governor limits.
Modifying your Apex
There are a number of situations when a solution like this might be helpful, but consider this situation; you want to move 10 million records from Source_Object__c to Target_Object__c via Apex. You would hit the governor limits on number of records retrieved via SOQL and the number of records processed via DML, just to name just a few.
Assuming there isn't already an autonumber field on Source_Object__c that could help us keep track of our progress processing the records, we'll first need to add a checkbox field to Source_Object__c called Processed__c.

We can then use that field in our SOQL query to ignore records already processed, and likewise set it to true as we process records. You would then need to modify your method with a few lines of code similar to what is in red below.


global class BatchProcessDemo {
webservice static void processItems() {
Integer queryLimit = (Limits.getLimitQueryRows() - Limits.getQueryRows()) / 2;
for (List<Source_Object__c> sourceItemList :[select Id, Color, Weight
from Source_Object__c
where Processed__c = false
limit :queryLimit ]) {
List<Target_Object__c> itemsToInsert = new List<Target_Object__c>();
for (Source_Object__c sourceItem : sourceItemList) {
sourceItem.Processed__c = true;
Target_Object__c targetItem = new Target_Object__c();
targetItem.Color__c = sourceItem.Color__c;
targetItem.Weight__c = sourceItem.Weight__c;
targetItem.Source_Object__c = sourceItem.Id;
if (Limits.getDMLRows() + itemsToInsert.size() + 1 >= Limits.getLimitDMLRows()) {
insert itemsToInsert;
}
itemsToInsert.add(targetItem);
}
update sourceItemList;
insert itemsToInsert;
}
}
}


Creating the Visualforce Page
As mentioned in a previous post by Frank and Kyle, make sure you have Development Mode enabled and then redirect your browser to http://server.salesforce.com/apex/BatchDemo to create your page. Click on Page Editor in the bottom left of the browser to open the Visualforce Editor. Add the following code between the <apex:page> tags to setup our form:

<apex:sectionHeader title="Demo"/>
<apex:form>
<apex:pageBlock title="Perform Batch Process">
<apex:panelGrid columns="2" id="theGrid">
<apex:outputLabel value="Max. # of Iterations"/>
<input type="text" value="1" name="iterations" id="iterations"/>
</apex:panelGrid>
</apex:pageBlock>
</apex:form>

You'll notice we use standard HTML input fields rather than Apex input fields since there is no VisualForce controller required. The fields will only be used on the client side via Javascript to batch our calls to Apex.
Add a <div> tag immediately after the </apex:panelGrid> tag to display progress during the batch processing.

<div id="progress" style="color: red"/>

After the <div> tag, add a button to allow us to kick off the processing.

<apex:pageBlockButtons >
<input type="button" onclick="batchProcess()" value="Start" class="btn"/>
</apex:pageBlockButtons>

Next, we'll need to define the batchProcess() method by adding the following code after the first <apex:page> tag.

<script language="javascript">
function batchProcess(){
var iterations = document.getElementById("iterations").value;
var progress = document.getElementById("progress");
progress.innerHTML = "Processing iteration 1 of " + iterations + " iterations.";
sforce.connection.sessionId = "{!$Api.Session_ID}"; //to avoid session timeout
for (i=1; i <= iterations; i++) {
progress.innerHTML = "Processing iteration " + i + " of " + iterations + " iterations.";
sforce.apex.execute("BatchProcessDemo","processItems ",{});
}
progress.innerHTML = "Completed processing " + iterations + " iterations!";
}
</script>

Click Save. Now you can click the Start button on your VisualForce page to perform the job in batches.

No comments: