Limits and Governance
Overcoming Scheduled Script Limitations
Recently when creating a scheduled script I ran into 2 limitations. Overcoming the first, introduced the second. The script I was writing was to send out a survey to our customers. The survey is sent out every other month to a different set of Customer/Contacts broken up by the first letter of the company name. For example in January we would send a survey to all contacts whose company name starts with either A or B. Who to send the survey to is determined by a saved search. In my example the script was written in Suitescript 1.0 because it was re-usign large parts of previously written scripts and libraries. Equivalent functions exist in SuiteScript 2.x
Problem 1: Too many Results
In my initial testing everything worked fine, however when looking at the actual saved search I noticed that there were 1,500+ contacts in the list and that the script only looked at the first 1000 results.
that limitation you can create a loop that brings back a subset of the records with each run. I found this code:
var search = nlapiLoadSearch('record_type', 'savedsearch_id');
var searchresults = search.runSearch();
var resultIndex = 0;
var resultStep = 1000;
var resultSet;
do {
resultSet = searchresults.getResults(resultIndex, resultIndex + resultStep); // retrieves all possible results up to the 1000 max returned
resultIndex = resultIndex + resultStep; // increment the starting point for the next batch of records
for(var i = 0; !!resultSet && i < resultSet.length; i++){ // loop through the search results
/* Insert your code here
Your code goes here to work on a the current resultSet (upto 1000 records per pass)
*
}
} while (resultSet.length > 0)
This solved my first problem, and in my initial testing everything worked, but as I added more code, updating records, sending the email, I introduced my second problem.
Problem 2: Script Execution Limits
For those of you that may not be aware, NetSuite has a way to limit and stop long running, or resource-intensive script. They call this API Governance. If a script exceeds the governance limit it will error out and stop running. In my experience the most common governance limit that I run into is the API limit. Each operation you perform in a script is assigned a unit cost, a scheduled script is limited to 10,000 units. For example sending an email costs 10 units. You can get a full list in the help file by searching “API Governance”
The first step is to determine how many governance units are being used in each iteration of your loop so you can determine when you are getting low.
var usageRemaining = context.getRemainingUsage();
nlapiLogExecution('DEBUG', 'Remaining Usage', usageRemaining);
In my example each iteration took approximately 90 units, so when my script had less than 100 units remaining I would want to do something. In my case I added a buffer increasing it to 300 to ensure I had enough units remaining to handle the rescheduling. There are several ways you can handle the limits.
In some scripts we mark a record as processed, so the next time the script and saved search run the record will drop out of the results.. In that instance in SuiteScript 2 you can use the task function to reschedule the script.
define(['N/task', 'N/runtime'], function(task, runtime) {
/**
* Reschedules the current script and returns the ID of the reschedule task
*/
function rescheduleCurrentScript() {
var scheduledScriptTask = task.create({
taskType: task.TaskType.SCHEDULED_SCRIPT
});
scheduledScriptTask.scriptId = runtime.getCurrentScript().id;
scheduledScriptTask.deploymentId = runtime.getCurrentScript().deploymentId;
return scheduledScriptTask.submit();
}
In my script I needed to continue where I left off, so I used the nlapiYieldScript function. I could have used the nlapiSchedule Script, however then I would need to keep track of where I left off and pass it to the scheduled script as a parameter. In using the Yeild function you just need to set the recovery point.
From the NetSuite help file:
nlapiYieldScript()
Creates a recovery point and, when executed, continues from the recovery point. The newly resubmitted script has its governance units reset. To summarize, nlapiYieldScript works as follows:
1. Creates a new recovery point.
2. Creates a new scheduled script instance with governance reset.
3. Associates the recovery point to the new instance of the scheduled script
4. Submits the new instance for processing.
In my example I could do the following:
if (nlapiGetContext().getRemainingUsage() < 300) {
var state = nlapiSetRecoveryPoint();
var stateMain = nlapiYieldScript();
}
This allowed my script to keep running and process all of the records in my saved search.
Until next time if there are any questions or comments, please feel free to reach out to me.
Thank You,
Mark Kreminski