Skip to content

Commit 777128b

Browse files
committed
Add comprehensive GlideRecord performance optimization techniques
- Advanced batch processing with chunked operations and memory management - Indexed field query optimization for maximum database performance - Query performance comparison framework with automated testing - Best practices for large dataset processing and pagination - Performance monitoring tools and analysis utilities - Memory-efficient operations and transaction management - Real-world examples with detailed documentation and use cases
1 parent 5804e23 commit 777128b

File tree

4 files changed

+976
-0
lines changed

4 files changed

+976
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# GlideRecord Performance Optimization Techniques
2+
3+
This collection provides advanced techniques for optimizing GlideRecord queries and database operations in ServiceNow.
4+
5+
## Overview
6+
7+
Performance optimization is crucial for maintaining responsive ServiceNow applications, especially when dealing with large datasets or complex queries. These snippets demonstrate best practices for efficient GlideRecord usage.
8+
9+
## Key Performance Principles
10+
11+
- **Minimize Database Roundtrips**: Use efficient query patterns
12+
- **Proper Indexing**: Leverage indexed fields in queries
13+
- **Batch Operations**: Process multiple records efficiently
14+
- **Query Optimization**: Use appropriate query methods
15+
- **Memory Management**: Handle large datasets responsibly
16+
17+
## Snippets Included
18+
19+
1. **optimized_batch_processing.js** - Efficient batch processing techniques
20+
2. **indexed_field_queries.js** - Leveraging database indexes
21+
3. **chunked_data_processing.js** - Processing large datasets in chunks
22+
4. **query_performance_comparison.js** - Performance comparison examples
23+
5. **memory_efficient_operations.js** - Memory-conscious GlideRecord usage
24+
25+
## Performance Monitoring
26+
27+
Always measure performance using:
28+
- `gs.log()` with timestamps for execution time
29+
- Database query metrics in Developer Tools
30+
- Performance Analytics for production monitoring
31+
32+
## Best Practices Summary
33+
34+
1. Always query on indexed fields when possible
35+
2. Use `setLimit()` for large result sets
36+
3. Avoid using `getRowCount()` on large tables
37+
4. Use `chooseWindow()` for pagination
38+
5. Consider using `GlideAggregate` for statistical queries
39+
6. Implement proper error handling and logging
40+
41+
## Related Documentation
42+
43+
- [ServiceNow GlideRecord API Documentation](https://developer.servicenow.com/dev.do#!/reference/api/tokyo/server/no-namespace/c_GlideRecordScopedAPI)
44+
- [Query Performance Best Practices](https://docs.servicenow.com/bundle/tokyo-platform-administration/page/administer/managing-data/concept/query-performance.html)
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
/**
2+
* Indexed Field Query Optimization
3+
*
4+
* This snippet demonstrates how to leverage database indexes for optimal query performance
5+
* in ServiceNow GlideRecord operations.
6+
*
7+
* Use Case: High-performance queries on large tables
8+
* Performance Benefits: Faster query execution, reduced database load
9+
*
10+
* @author ServiceNow Community
11+
* @version 1.0
12+
*/
13+
14+
// Method 1: Using Indexed Fields for Optimal Performance
15+
function queryWithIndexedFields() {
16+
var gr = new GlideRecord('incident');
17+
18+
// GOOD: Query on indexed fields (state, priority, assignment_group are typically indexed)
19+
gr.addQuery('state', 'IN', '2,3'); // Work in Progress, On Hold
20+
gr.addQuery('priority', '<=', '3'); // High priority and above
21+
gr.addQuery('assignment_group', '!=', ''); // Has assignment group
22+
23+
// GOOD: Use sys_created_on for date ranges (indexed timestamp)
24+
gr.addQuery('sys_created_on', '>=', gs.daysAgoStart(7));
25+
gr.addQuery('sys_created_on', '<=', gs.daysAgoEnd(1));
26+
27+
// GOOD: Order by indexed field
28+
gr.orderBy('sys_created_on');
29+
30+
gr.query();
31+
32+
var results = [];
33+
while (gr.next()) {
34+
results.push({
35+
number: gr.getDisplayValue('number'),
36+
state: gr.getDisplayValue('state'),
37+
priority: gr.getDisplayValue('priority')
38+
});
39+
}
40+
41+
return results;
42+
}
43+
44+
// Method 2: Avoiding Non-Indexed Field Queries
45+
function avoidNonIndexedQueries() {
46+
// BAD EXAMPLE - Don't do this on large tables
47+
function badQueryExample() {
48+
var gr = new GlideRecord('incident');
49+
50+
// BAD: Contains query on non-indexed text field
51+
gr.addQuery('description', 'CONTAINS', 'network issue');
52+
53+
// BAD: Complex wildcard search
54+
gr.addQuery('short_description', 'STARTSWITH', 'Unable');
55+
56+
gr.query();
57+
return gr.getRowCount(); // Also bad on large tables
58+
}
59+
60+
// GOOD ALTERNATIVE - Use indexed fields and full-text search
61+
function goodQueryAlternative() {
62+
var gr = new GlideRecord('incident');
63+
64+
// GOOD: Use category/subcategory (often indexed)
65+
gr.addQuery('category', 'network');
66+
67+
// GOOD: Use indexed fields first to narrow results
68+
gr.addQuery('state', 'IN', '1,2,3');
69+
gr.addQuery('sys_created_on', '>=', gs.daysAgoStart(30));
70+
71+
// Then filter on text if needed (smaller result set)
72+
gr.addQuery('short_description', 'CONTAINS', 'unable');
73+
74+
gr.query();
75+
76+
var results = [];
77+
while (gr.next()) {
78+
results.push(gr.getUniqueValue());
79+
}
80+
81+
return results;
82+
}
83+
84+
return goodQueryAlternative();
85+
}
86+
87+
// Method 3: Compound Index Optimization
88+
function optimizeCompoundIndexes() {
89+
var gr = new GlideRecord('task');
90+
91+
// GOOD: Query fields in order that matches compound indexes
92+
// Many tables have compound indexes on (table, state, assigned_to)
93+
gr.addQuery('state', '!=', '7'); // Not closed
94+
gr.addQuery('assigned_to', '!=', ''); // Has assignee
95+
96+
// Add additional filters after indexed ones
97+
gr.addQuery('priority', '<=', '3');
98+
99+
// Order by indexed field for better performance
100+
gr.orderBy('sys_updated_on');
101+
102+
gr.query();
103+
104+
return gr.getRowCount();
105+
}
106+
107+
// Method 4: Reference Field Optimization
108+
function optimizeReferenceQueries() {
109+
// GOOD: Query reference fields by sys_id (indexed)
110+
function queryByReferenceId() {
111+
var groupSysId = getAssignmentGroupSysId('Hardware');
112+
113+
var gr = new GlideRecord('incident');
114+
gr.addQuery('assignment_group', groupSysId); // Uses index
115+
gr.addQuery('state', '!=', '7'); // Not closed
116+
gr.query();
117+
118+
return gr.getRowCount();
119+
}
120+
121+
// LESS OPTIMAL: Query by reference field display value
122+
function queryByDisplayValue() {
123+
var gr = new GlideRecord('incident');
124+
gr.addQuery('assignment_group.name', 'Hardware'); // Less efficient
125+
gr.addQuery('state', '!=', '7');
126+
gr.query();
127+
128+
return gr.getRowCount();
129+
}
130+
131+
// BEST: Combine both approaches
132+
function optimizedReferenceQuery() {
133+
// First get the sys_id using indexed query
134+
var groupGR = new GlideRecord('sys_user_group');
135+
groupGR.addQuery('name', 'Hardware');
136+
groupGR.query();
137+
138+
if (groupGR.next()) {
139+
var groupSysId = groupGR.getUniqueValue();
140+
141+
// Then use sys_id in main query (indexed)
142+
var gr = new GlideRecord('incident');
143+
gr.addQuery('assignment_group', groupSysId);
144+
gr.addQuery('state', '!=', '7');
145+
gr.query();
146+
147+
return gr.getRowCount();
148+
}
149+
150+
return 0;
151+
}
152+
153+
return optimizedReferenceQuery();
154+
}
155+
156+
// Method 5: Date Range Optimization
157+
function optimizeDateRangeQueries() {
158+
// GOOD: Use built-in date functions with indexed timestamps
159+
function efficientDateQuery() {
160+
var gr = new GlideRecord('incident');
161+
162+
// Use sys_created_on (indexed) with built-in functions
163+
gr.addQuery('sys_created_on', '>=', gs.daysAgoStart(30));
164+
gr.addQuery('sys_created_on', '<=', gs.daysAgoEnd(1));
165+
166+
// Add other indexed filters
167+
gr.addQuery('state', '!=', '7');
168+
169+
gr.query();
170+
return gr.getRowCount();
171+
}
172+
173+
// LESS OPTIMAL: Complex date calculations
174+
function lessOptimalDateQuery() {
175+
var gr = new GlideRecord('incident');
176+
177+
// Complex date calculation (harder to optimize)
178+
var thirtyDaysAgo = new GlideDateTime();
179+
thirtyDaysAgo.addDaysUTC(-30);
180+
181+
gr.addQuery('sys_created_on', '>=', thirtyDaysAgo);
182+
gr.query();
183+
184+
return gr.getRowCount();
185+
}
186+
187+
return efficientDateQuery();
188+
}
189+
190+
// Method 6: Query Performance Analysis
191+
function analyzeQueryPerformance() {
192+
var queries = [
193+
{
194+
name: 'Indexed Query',
195+
query: function() {
196+
var gr = new GlideRecord('incident');
197+
gr.addQuery('state', '2');
198+
gr.addQuery('priority', '1');
199+
gr.setLimit(100);
200+
gr.query();
201+
return gr.getRowCount();
202+
}
203+
},
204+
{
205+
name: 'Non-Indexed Query',
206+
query: function() {
207+
var gr = new GlideRecord('incident');
208+
gr.addQuery('description', 'CONTAINS', 'test');
209+
gr.setLimit(100);
210+
gr.query();
211+
return gr.getRowCount();
212+
}
213+
}
214+
];
215+
216+
queries.forEach(function(queryTest) {
217+
var startTime = new Date().getTime();
218+
var result = queryTest.query();
219+
var endTime = new Date().getTime();
220+
var executionTime = endTime - startTime;
221+
222+
gs.log(queryTest.name + ':');
223+
gs.log(' Execution time: ' + executionTime + 'ms');
224+
gs.log(' Result count: ' + result);
225+
gs.log(' Performance rating: ' + (executionTime < 100 ? 'Good' : executionTime < 500 ? 'Fair' : 'Poor'));
226+
});
227+
}
228+
229+
// Helper function
230+
function getAssignmentGroupSysId(groupName) {
231+
var gr = new GlideRecord('sys_user_group');
232+
gr.addQuery('name', groupName);
233+
gr.query();
234+
235+
if (gr.next()) {
236+
return gr.getUniqueValue();
237+
}
238+
239+
return '';
240+
}
241+
242+
// Method 7: Index-Aware Pagination
243+
function indexAwarePagination(pageSize, pageNumber) {
244+
pageSize = pageSize || 50;
245+
pageNumber = pageNumber || 0;
246+
247+
var gr = new GlideRecord('incident');
248+
249+
// Use indexed fields for filtering
250+
gr.addQuery('state', 'IN', '1,2,3');
251+
gr.addQuery('sys_created_on', '>=', gs.daysAgoStart(90));
252+
253+
// Order by indexed field for consistent pagination
254+
gr.orderBy('sys_created_on');
255+
gr.orderByDesc('sys_id'); // Secondary sort for tie-breaking
256+
257+
// Use chooseWindow for efficient pagination
258+
gr.chooseWindow(pageNumber * pageSize, (pageNumber + 1) * pageSize);
259+
260+
gr.query();
261+
262+
var results = [];
263+
while (gr.next()) {
264+
results.push({
265+
sys_id: gr.getUniqueValue(),
266+
number: gr.getDisplayValue('number'),
267+
short_description: gr.getDisplayValue('short_description'),
268+
state: gr.getDisplayValue('state')
269+
});
270+
}
271+
272+
return {
273+
data: results,
274+
page: pageNumber,
275+
pageSize: pageSize,
276+
hasMore: results.length === pageSize
277+
};
278+
}

0 commit comments

Comments
 (0)