- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
Improving ServiceNow Performance with Time & Space Complexity Considerations.
We often emphasize coding best practices: reusable Script Includes, Scoped App, ACLs, scalable design. Yet one critical dimension is overlooked - algorithmic efficiency
Writing brute-force code may fulfil requirements quickly, but it can cripple performance as data grows. Inefficient scripts don't just slow one user; they consume semaphore nodes, leading to instance-wide latency or outages
Scaling an instance isn't just about Script Includes or Scoped App; it's about Computational Efficiency.
In this post, we'll move beyond basic coding standards and dive into the world of Big O Notation, JavaScript Object, and Functional Programming to ensure your instance remains lightning-fast.
When we write a nested loop to compare two datasets, we aren't just writing "bad code" - we are creating a O(n^2) time-bomb. As your CMDB grows from 1,000 to 100,000 records, that script doesn't just get a little slower; it becomes an exponential burden that can freeze an entire application node.
You've likely seen the dreaded error:
“Transaction Cancelled: maximum execution time exceeded.”
This happens when scripts run beyond the 5-minute quota, often due to nested loops or excessive GlideRecord queries.
Scenario: The "Audit & Update"
The Requirement: We have an external staging table with 5,000 "Server Status" updates. We need to find the corresponding CI in the cmdb_ci_server table (which has 150,000 records) and update the "Last Checked" date.
-> Approach A: The Brute Force (Nested GlideRecord Queries):
This approach is "logical" but architecturally disastrous. It uses a nested query pattern.
// O(N * M) Complexity - The "Transaction timeout"
var staging = new GlideRecord('u_server_updates');
staging.query();
while (staging.next()) {
// For EVERY staging record, we hit the database again
var ci = new GlideRecord('cmdb_ci_server');
ci.addQuery('serial_number', staging.u_serial);
ci.query();
if (ci.next()) {
ci.u_last_checked = new GlideDateTime();
ci.update();
}
}
The Problem: If there are 5,000 staging rows, this script performs 5,001 database queries. The database "handshake" overhead alone will likely exceed the transaction limit.
Complexity: O(n*m)
-> Approach B: The Optimized Solution (JavaScript Object Pattern)
Here, we apply a Space-Time Trade-off. We use a little more memory (Space) to achieve a massive gain in speed (Time).
// O(N + M) Complexity - The "Scalable" Approach
var serverMap = {};
var serials = [];
// 1. Collect all serials first - O(N)
var staging = new GlideRecord('u_server_updates');
staging.query();
while (staging.next()) {
var s = staging.getValue('u_serial');
serials.push(s);
serverMap[s] = true; // Use a JavaScript Object for O(1) lookups later
}
// 2. Perform ONE bulk query - O(M)
var ci = new GlideRecord('cmdb_ci_server');
ci.addQuery('serial_number', 'IN', serials);
ci.query();
while (ci.next()) {
// 3. Constant time lookup
if (serverMap[ci.serial_number]) {
ci.u_last_checked = new GlideDateTime();
ci.setWorkflow(false); // Optimization: Skip Business Rules
ci.update();
}
}The Result: We reduced 5,000 queries to exactly
Complexity: O(n + m) This script will finish in seconds, not minutes.
->Best Practices for Performance:
Minimize GlideRecord queries: Use addQuery, addExtraFields, and bulk queries.
Avoid nested loops: Replace with hash maps or sets.
Index fields: Query on indexed fields to avoid full table scans.
Batch processing with async mechanism: Use Script Action along with eventScheduler or scheduled jobs for large datasets.
Measure complexity: Think in terms of Big O notation before coding.
Verdict
Efficiency isn't a "nice to have" - it's a requirement for enterprise stability. If you aren't thinking about how your script will behave when the table hits 1 million rows, you aren't building for the future: you're just building technical debt.
As ServiceNow professionals, our job isn't just to make the "Submit" button work. Our job is to ensure the system remains fast as the company grows.
Next time you write a script, ask yourself:
Is there a query inside this loop? (Target: 0)
Am I fetching fields I don't need?
Can I use a JavaScript Object or Set Difference instead of a nested loop?
Performance isn't a feature you add at the end; it’s a mindset you apply at the start.
Thanks,
Ratnakar
- 447 Views
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
