Scheduled Script Handling of Server Restarts
Sometimes, a scheduled script might fail if the application server restarts—like for a NetSuite update, maintenance, or an unexpected issue. Restarts can force a script to stop anytime, so your scripts should handle restarts and recover from interruptions.
In SuiteScript 2.x, if a failure happens, the script restarts from the beginning. NetSuite knows when a scheduled script was stopped and restarts it as soon as the needed resources are ready. You don’t manually script yields or recovery points in SuiteScript 2.x, so it’s easier to manage restarts.
In your scheduled script, check the context.type
attribute to see if the script was restarted (if context.type === "aborted")
. For example:
/**
* @NApiVersion 2.x
* @NScriptType ScheduledScript
*/
define([], function(){
return {
execute: function (context)
{
if (context.type === 'aborted')
{
// I might do something differently
}
.
.
.
}
}
});
Want more details about the context argument? See context.InvocationType.
The following example scripts demonstrate how restarts impact scheduled script execution:
Example: A Problematic Scheduled Script
Scheduled scripts often search for data, then process the results.
In this example, the script updates each sales order in the system. The filter1
filter ensures that the search returns exactly one entry per sales order. But if the script is stopped in the middle and restarted, some sales orders might get updated twice.
To avoid data issues from re-processing, make your script’s operations idempotent—they should give the same result even if run more than one time (like stopping duplicate records). Or, your script should be able to fix mistakes, like removing duplicates if they happen.
The example script isn’t idempotent, so many sales orders could get updated twice—possibly doubling a price from a repeated run.
/**
* @NApiVersion 2.x
* @NScriptType ScheduledScript
*/
define(['N/search', 'N/record'],
function(search, record){
return {
execute: function (context)
{
var filter1 = search.createFilter({
name: 'mainline',
operator: search.Operator.IS,
values: true
});
var srch = search.create({
type: search.Type.SALES_ORDER,
filters: [filter1],
columns: []
});
var pagedResults = srch.runPaged();
pagedResults.pageRanges.forEach(function(pageRange){
var currentPage = pagedResults.fetch({index: pageRange.index});
currentPage.data.forEach(function(result){
var so = record.load({
type: record.Type.SALES_ORDER,
id: result.id
});
//UPDATE so FIELDS>
so.save()
});
});
}
}
});
To fix scripts like this, check out Example: A Robust Scheduled Script and Example 5: A Robust Map/Reduce Script Example.
Example: A Robust Scheduled Script
The following example shows how to prevent duplicate processing if your script restarts. This script uses custbody_processed_flag
on the sales order. It is a custom boolean field that must be previously created in the UI. When a sales order is processed, the field is set to true
, so your search skips flagged sales orders. If the script restarts, only unprocessed sales orders are handled.
/**
* @NApiVersion 2.x
* @NScriptType ScheduledScript
*/
define(['N/search', 'N/record'],
function(search, record){
return {
execute: function (context)
{
var filter1 = search.createFilter({
name: 'mainline',
operator: search.Operator.IS,
values: true
});
var filter2 = search.createFilter({
name: 'custbody_processed_flag',
operator: search.Operator.IS,
values: false
});
var srch = search.create({
type: search.Type.SALES_ORDER,
filters: [filter1, filter2],
columns: []
});
var pagedResults = srch.runPaged();
pagedResults.pageRanges.forEach(function(pageRange){
var currentPage = pagedResults.fetch({index: pageRange.index});
currentPage.data.forEach(function(result){
var so = record.load({
type: record.Type.SALES_ORDER,
id: result.id
});
// UPDATE so FIELDS
so.setValue({
fieldID: 'custbody_processed_flag',
value: true
});
so.save()
});
});
}
}
});