🔹 Why Performance Optimization Matters
NetSuite scripts run on usage governance limits and system resources. Poorly optimized scripts can:
❌ Timeout or exceed governance limits
❌ Lock records and slow down transactions
❌ Cause errors during peak loads
❌ Deliver poor user experience
Optimizing scripts ensures:
âś… Faster execution
âś… Lower governance usage
âś… Scalability for large datasets
âś… Better reliability
🔹 Key Areas to Optimize
- Governance Management
- Each API call consumes governance units.
- Example:
record.load()
costs more than usingsearch.lookupFields()
.
- Efficient Searches
- Use Saved Searches or SuiteQL instead of looping
search.run().each()
. - Fetch only required fields.
- Use paged searches for large result sets.
- Use Saved Searches or SuiteQL instead of looping
- Bulk Processing
- Use Map/Reduce scripts instead of Scheduled scripts for large data.
- Use getInputData efficiently to stream search results.
- Minimal Record Loads
- Avoid loading entire records when only one field is needed.
- Use
lookupFields
orsearch.lookupFields
.
- Caching
- Store reusable data (e.g., exchange rates, account IDs) with
N/cache
. - Reduces repeated lookups.
- Store reusable data (e.g., exchange rates, account IDs) with
- Asynchronous Work
- Offload heavy processing with Map/Reduce or Scheduled Scripts.
- Trigger async work from User Event scripts.
- UI Responsiveness (Suitelets & Client Scripts)
- Keep Suitelets lightweight: show progress and push bulk processing to Map/Reduce.
- Use client validation instead of server calls where possible.
🔹 Code Examples
1) Expensive (Bad) vs Optimized (Good)
❌ Bad: Loading full record for one field
// Costly approach
var rec = record.load({ type: 'customer', id: 123 });
var email = rec.getValue('email');
âś… Good: Lookup only needed field
// Efficient approach
var email = search.lookupFields({
type: 'customer',
id: 123,
columns: ['email']
}).email;
2) Using SuiteQL for Bulk Fetch
/**
* Example: Fetch customers with SuiteQL
*/
const sql = `
SELECT id, entityid, email
FROM customer
WHERE isinactive = 'F'
LIMIT 100
`;
const results = query.runSuiteQL({ query: sql }).asMappedResults();
results.forEach(r => {
log.debug('Customer', r.entityid + ' | ' + r.email);
});
âś… Faster than running searches with multiple joins.
3) Using Caching (N/cache)
/**
* Example: Cache exchange rate lookups
*/
define(['N/cache'], cache => {
const exchangeCache = cache.getCache({ name: 'exchange_rates' });
function getRate(currencyId) {
return exchangeCache.get({
key: currencyId,
loader: () => {
// Simulate API call or record lookup
log.debug('Cache Miss', 'Fetching from source');
return '1.25';
}
});
}
return { getRate };
});
âś… Prevents repeated expensive lookups.
4) Governance Handling in Map/Reduce
/**
* Example: Yield execution if governance is low
*/
function map(context) {
try {
if (runtime.getCurrentScript().getRemainingUsage() < 200) {
// Let NetSuite auto-resume
return;
}
// Process logic here
} catch (e) {
log.error('Error in Map', e);
}
}
🔹 Best Practices Checklist
- âś… Use Map/Reduce for big data (invoices, orders, journal entries).
- âś… Avoid synchronous loops over thousands of records.
- âś… Use lookupFields & SuiteQL instead of
record.load()
. - âś… Batch writes instead of saving one record at a time.
- âś… Log smartly (only key info, avoid logging inside huge loops).
- âś… Use cache for reference data.
- âś… Design for resumability (scripts should pick up where they left off).
âś… Summary
Performance optimization in NetSuite scripting is about working smarter, not harder.
By managing governance units, leveraging SuiteQL, caching, and Map/Reduce, you ensure your solutions are scalable, fast, and reliable.