Introduction
When working with thousands of records in NetSuite β invoices, sales orders, or journal entries β efficiency becomes critical.
Poorly optimized scripts can exceed governance limits, hit timeouts, or cause concurrency errors.
This tutorial walks you through practical performance optimization patterns for Map/Reduce and Scheduled scripts β proven approaches that large-scale NetSuite implementations (like EDI or Shopify integrations) rely on every day.
π‘ Why Script Optimization Matters
Challenge | Without Optimization | With Optimization |
---|---|---|
Governance Limits | Script stops mid-run | Smart rescheduling continues automatically |
Large Datasets | Timeouts or memory errors | Efficient chunking and batching |
Slow Performance | Long execution times | Parallelized Map stage and caching |
Debug Difficulty | Hard to trace issues | Structured logging and checkpoints |
π§± Step 1: Choose the Right Script Type
Script Type | Best For | Key Advantage |
---|---|---|
Map/Reduce | Large record sets (10k+) | Parallelized processing |
Scheduled Script | Smaller batches | Simpler control & logic |
Workflow Action Script | Single record updates | Trigger-based automation |
π‘ Tip: Use Map/Reduce for high-volume jobs (search results > 5,000 rows).
βοΈ Step 2: Optimize Data Retrieval
1οΈβ£ Use Saved Searches Efficiently
- Always define explicit filters (e.g.,
mainline = true
) - Return only required columns
- Avoid
search.run().getRange(0, 1000)
loops β instead use Paged Search API
const searchObj = search.load({ id: 'customsearch_large_txn' });
const pagedData = searchObj.runPaged({ pageSize: 1000 });
pagedData.pageRanges.forEach(page => {
const currentPage = pagedData.fetch({ index: page.index });
currentPage.data.forEach(result => {
// process result
});
});
β Prevents governance overuse and memory overflow.
βοΈ Step 3: Use Map/Reduce for Parallel Processing
Map/Reduce Structure Example
define(['N/search', 'N/record', 'N/runtime'], (search, record, runtime) => {
const getInputData = () => search.load({ id: 'customsearch_invoices' });
const map = (ctx) => {
const data = JSON.parse(ctx.value);
const id = data.id;
try {
record.submitFields({
type: 'invoice',
id,
values: { custbody_processed: true }
});
} catch (e) {
log.error('Map Error', e);
}
};
const summarize = (summary) => {
log.audit('Completed', summary);
};
return { getInputData, map, summarize };
});
β Map stage runs in parallel β faster execution across multiple queues.
βοΈ Step 4: Reduce Governance Consumption
Technique | Description |
---|---|
Bulk Operations | Use record.submitFields() instead of record.load() β 1 unit vs 10+ units |
Cache Reusable Data | Use N/cache to store lookups for customers, items, etc. |
Avoid Unnecessary Loads | Donβt load a record if you only need one field |
Use search.lookupFields() | Lightweight data access |
Batch Commits | Group 100+ updates, then reschedule or summarize |
π§ Example: Using N/cache
for Lookup Optimization
const cacheObj = cache.getCache({ name: 'cust_cache' });
let customerName = cacheObj.get({ key: 'cust_123' });
if (!customerName) {
customerName = search.lookupFields({
type: 'customer', id: 123, columns: ['companyname']
}).companyname;
cacheObj.put({ key: 'cust_123', value: customerName, ttl: 300 });
}
β Reduces repeated searches and speeds up execution.
π Step 5: Auto-Reschedule on Governance Exhaustion
Add this pattern to long-running loops:
if (runtime.getCurrentScript().getRemainingUsage() < 200) {
log.audit('Rescheduling Script');
task.create({
taskType: task.TaskType.SCHEDULED_SCRIPT,
scriptId: runtime.getCurrentScript().id,
deploymentId: runtime.getCurrentScript().deploymentId
}).submit();
return;
}
β Prevents βSSS_USAGE_LIMIT_EXCEEDEDβ errors.
π Step 6: Optimize Map/Reduce Summary Stage
The summarize() stage can:
- Aggregate totals
- Send summary emails
- Log performance metrics
const summarize = (summary) => {
log.audit('Usage Remaining', runtime.getCurrentScript().getRemainingUsage());
summary.output.iterator().each((key, value) => {
log.audit('Processed Key', key);
return true;
});
};
β Helps with analytics and debugging post-run.
π§© Step 7: Use Governance Monitoring
Create a Custom Record: Script Performance Log
Field | Description |
---|---|
Script ID | Internal Script ID |
Execution Time | Duration in seconds |
Records Processed | Count per run |
Errors | Count |
Reschedules | Count |
Track performance trends via Saved Search or Dashboard Portlet.
π§° Step 8: Debugging Efficiently
β
Use log.audit()
for checkpoints instead of log.debug()
in high-volume loops.
β
Wrap key functions in try/catch and continue iteration.
β
Store error summaries in reduce summary stage.
β
Export errors to CSV or Custom Record for long-term review.
β‘ Step 9: Advanced Tips
Technique | Description |
---|---|
Concurrent Deployments | Run same Map/Reduce script on different saved searches for parallelism. |
Selective Queues | Use runtime.queueCount to avoid overloading the queue. |
Chunking by ID Range | Use script parameters startId / endId to divide workload. |
Memory Efficiency | Avoid storing full JSON datasets in arrays; process in-place. |
Governance Logging | Log usage before and after each major operation. |
π§© Step 10: Real-World Example β Processing 100,000 Invoices
Scenario:
You need to mark 100k invoices as βSyncedβ after integration with an external system.
Solution:
- Use Map/Reduce script
- Load a saved search of invoices
- Mark each in map() using
submitFields()
- Log every 5,000 records
- Auto-reschedule when
usage < 200
Outcome:
β
Completed 100,000+ records with zero timeouts.
β
Reduced runtime from 2 hours β 18 minutes.
π Related Tutorials
- π Custom Error Handling & Retry Framework
- π Integration Logging Dashboard
- π NetSuite Deployment & Version Control Strategy
β FAQ
Q1. Why use Map/Reduce instead of Scheduled Scripts?
Map/Reduce allows parallel record processing across queues, improving performance for large jobs.
Q2. How can I handle 1M+ records safely?
Break into smaller saved search chunks and process by internal ID range.
Q3. How to debug failed map/reduce executions?
Check the Script Execution Logs β Map/Reduce Status Page for stage-specific error messages.
Q4. Does governance reset between stages?
Yes β each stage (Get Input, Map, Reduce, Summarize) has separate governance usage limits.
π§ Summary
Performance optimization is not just about avoiding timeouts β itβs about scaling NetSuite processes intelligently.
By applying batching, caching, and auto-rescheduling techniques, your scripts can handle hundreds of thousands of records reliably while maintaining audit-friendly traceability.
These optimization patterns form the backbone of enterprise-level SuiteScript solutions β powering integrations, EDI processes, and financial automations efficiently.
Leave a Reply