Complete Guide to ServiceNow Performance Monitoring and Optimization
Complete Guide to ServiceNow Performance Monitoring and Optimization
Performance monitoring is critical for maintaining a healthy ServiceNow instance. Slow performance can impact user productivity, SLA compliance, and overall business operations. This comprehensive guide covers ServiceNow's built-in monitoring tools, optimization strategies, and best practices for keeping your instance running at peak performance.
Understanding ServiceNow Performance Metrics
Key Performance Indicators (KPIs)
Response Time Metrics:
- Page load times (target: <3 seconds)
- API response times (target: <1 second)
- Database query execution times
- Script execution duration
Resource Utilization:
- CPU usage patterns
- Memory consumption
- Database connection pools
- Thread pool utilization
User Experience Metrics:
- Session timeouts
- Concurrent user load
- Transaction success rates
- Error frequencies
Built-in Performance Monitoring Tools
1. Performance Analytics Dashboard
Navigate to System Definition > Performance Analytics > Dashboards to access comprehensive performance insights.
Key Widgets to Monitor:
- Response Time Trends
- Database Performance
- Slow Transactions
- Error Rate Analysis
// Script to create custom performance metric
var gr = new GlideRecord('sys_performance');
gr.addQuery('transaction', 'incident.do');
gr.addQuery('sys_created_on', '>', gs.daysAgoStart(7));
gr.orderByDesc('response_time');
gr.setLimit(20);
gr.query();
while (gr.next()) {
gs.info('Slow transaction: ' + gr.url + ' - ' + gr.response_time + 'ms');
}
2. Stats Module
Access System Diagnostics > Stats for real-time system metrics.
Critical Stats to Monitor:
glide.db.max_connections.usedglide.threads.availableglide.cache.sizeglide.memory.heap.used
3. System Logs and Metrics
Application Log Analysis:
// Query for performance-related log entries
var gr = new GlideRecord('syslog');
gr.addQuery('level', 'error');
gr.addQuery('source', 'CONTAINS', 'performance');
gr.addQuery('sys_created_on', '>', gs.daysAgoStart(1));
gr.orderByDesc('sys_created_on');
gr.query();
while (gr.next()) {
gs.info('Performance Error: ' + gr.message);
}
Database Performance Optimization
Query Optimization Strategies
1. Index Analysis and Creation:
// Find tables without proper indexing
var gr = new GlideRecord('sys_db_table');
gr.query();
while (gr.next()) {
var indexCount = new GlideRecord('sys_db_index');
indexCount.addQuery('table', gr.sys_id);
indexCount.query();
if (indexCount.getRowCount() < 2) {
gs.info('Table may need indexing: ' + gr.name);
}
}
2. Slow Query Detection:
Enable slow query logging in your instance properties:
glide.db.debug.log.query.threshold= 5000 (log queries > 5 seconds)glide.db.query.timeout= 30 (timeout after 30 seconds)
Database Maintenance Best Practices
Regular Cleanup Procedures:
- Archive old records using cleanup jobs
- Monitor table growth trends
- Implement retention policies
- Regular database statistics updates
// Example: Auto-cleanup script for old sys_email records
var cleanup = new GlideRecord('sys_email');
cleanup.addQuery('sys_created_on', '<', gs.daysAgoEnd(90));
cleanup.addQuery('state', 'sent');
cleanup.setLimit(1000);
cleanup.query();
var deleteCount = 0;
while (cleanup.next()) {
cleanup.deleteRecord();
deleteCount++;
}
gs.info('Cleaned up ' + deleteCount + ' old email records');
Script Performance Optimization
Business Rules Optimization
Performance Best Practices:
- Use appropriate timing: Use 'before' rules for validation, 'after' for notifications
- Minimize database calls: Batch operations when possible
- Avoid unnecessary queries: Use
currentobject properties instead of new queries
Optimized Business Rule Example:
// BAD: Multiple database calls
function onChange(control, oldValue, newValue, isLoading) {
if (current.state == '6') {
var user = new GlideRecord('sys_user');
user.get(current.assigned_to);
var manager = new GlideRecord('sys_user');
manager.get(user.manager);
// Send notification
}
}
// GOOD: Single optimized query
function onChange(control, oldValue, newValue, isLoading) {
if (current.state == '6') {
var userGR = new GlideRecord('sys_user');
userGR.addQuery('sys_id', current.assigned_to);
userGR.query();
if (userGR.next()) {
var managerEmail = userGR.manager.getDisplayValue();
// Process with manager info
}
}
}
Script Include Optimization
Caching Strategies:
// Implement caching for frequently accessed data
var CacheUtils = Class.create();
CacheUtils.prototype = {
initialize: function() {
this.cache = {};
this.cacheTimeout = 300000; // 5 minutes
},
getUserRoles: function(userSysId) {
var cacheKey = 'user_roles_' + userSysId;
var cached = this.cache[cacheKey];
if (cached && (gs.now().getTime() - cached.timestamp) < this.cacheTimeout) {
return cached.data;
}
// Fetch fresh data
var roles = [];
var gr = new GlideRecord('sys_user_has_role');
gr.addQuery('user', userSysId);
gr.query();
while (gr.next()) {
roles.push(gr.role.name.toString());
}
// Cache the results
this.cache[cacheKey] = {
data: roles,
timestamp: gs.now().getTime()
};
return roles;
},
type: 'CacheUtils'
};
Memory Management and Resource Optimization
Session Management
Monitor Session Health:
// Script to identify long-running sessions
var sessions = new GlideRecord('sys_user_session');
sessions.addQuery('sys_created_on', '<', gs.daysAgoStart(1));
sessions.addQuery('logged_out', 'false');
sessions.query();
while (sessions.next()) {
gs.info('Long session: User=' + sessions.user.name +
', Duration=' + sessions.sys_created_on.dateNumericValue());
}
Cache Optimization
Configure Instance Caching:
glide.cache.default.size= 2048glide.cache.max_size= 4096glide.ui.cache.list_edit.lifetime= 1800
Memory Leak Prevention
Common Memory Leak Patterns to Avoid:
// BAD: Memory leak in scheduled job
function executeMyJob() {
var largeArray = [];
var gr = new GlideRecord('large_table');
gr.query();
while (gr.next()) {
largeArray.push(gr); // Keeping references to GlideRecord objects
}
// Process array... but references remain in memory
}
// GOOD: Process records incrementally
function executeMyJob() {
var gr = new GlideRecord('large_table');
gr.query();
while (gr.next()) {
// Process individual record
processRecord(gr.getValue('field1'), gr.getValue('field2'));
// GlideRecord automatically dereferenced each iteration
}
}
Monitoring and Alerting Setup
Custom Performance Monitors
Create Performance Monitoring Scripts:
// Scheduled Script: Performance Health Check
var PerformanceMonitor = Class.create();
PerformanceMonitor.prototype = {
initialize: function() {
this.thresholds = {
dbConnections: 80, // Percentage of max connections
heapMemory: 85, // Percentage of heap
responseTime: 3000 // Milliseconds
};
},
checkDatabaseConnections: function() {
var used = parseInt(gs.getProperty('glide.db.max_connections.used', '0'));
var max = parseInt(gs.getProperty('glide.db.max_connections', '20'));
var percentage = (used / max) * 100;
if (percentage > this.thresholds.dbConnections) {
this.createAlert('High database connection usage: ' + percentage + '%');
}
return percentage;
},
checkMemoryUsage: function() {
var heapUsed = parseInt(gs.getProperty('glide.memory.heap.used', '0'));
var heapMax = parseInt(gs.getProperty('glide.memory.heap.max', '1'));
var percentage = (heapUsed / heapMax) * 100;
if (percentage > this.thresholds.heapMemory) {
this.createAlert('High memory usage: ' + percentage + '%');
}
return percentage;
},
createAlert: function(message) {
var alert = new GlideRecord('sys_email');
alert.initialize();
alert.recipients = 'admin@company.com';
alert.subject = 'ServiceNow Performance Alert';
alert.body = message + '\n\nInstance: ' + gs.getProperty('instance_name');
alert.insert();
},
type: 'PerformanceMonitor'
};
// Execute monitoring
var monitor = new PerformanceMonitor();
monitor.checkDatabaseConnections();
monitor.checkMemoryUsage();
Performance Dashboard Creation
Build Custom Performance Widgets:
// Widget Script for Performance Dashboard
(function() {
// Fetch performance data
var data = {};
// Response time data
var responseGR = new GlideRecord('sys_performance');
responseGR.addQuery('sys_created_on', '>', gs.daysAgoStart(7));
responseGR.orderBy('sys_created_on');
responseGR.query();
data.responseTimes = [];
while (responseGR.next()) {
data.responseTimes.push({
timestamp: responseGR.sys_created_on.toString(),
responseTime: responseGR.response_time.toString(),
url: responseGR.url.toString()
});
}
// Memory usage data
data.memoryStats = {
heapUsed: gs.getProperty('glide.memory.heap.used'),
heapMax: gs.getProperty('glide.memory.heap.max'),
connections: gs.getProperty('glide.db.max_connections.used')
};
return data;
})();
Performance Testing and Benchmarking
Load Testing Strategies
Automated Performance Tests:
// Performance test script for critical transactions
var PerformanceTest = Class.create();
PerformanceTest.prototype = {
initialize: function() {
this.results = [];
},
testIncidentCreation: function() {
var startTime = new Date().getTime();
// Create test incident
var incident = new GlideRecord('incident');
incident.initialize();
incident.short_description = 'Performance test incident';
incident.description = 'Created for performance testing';
incident.urgency = '3';
incident.impact = '3';
var incidentId = incident.insert();
var endTime = new Date().getTime();
var duration = endTime - startTime;
this.results.push({
transaction: 'incident_creation',
duration: duration,
timestamp: new Date()
});
// Cleanup
if (incidentId) {
incident.get(incidentId);
incident.deleteRecord();
}
return duration;
},
generateReport: function() {
var avgDuration = 0;
for (var i = 0; i < this.results.length; i++) {
avgDuration += this.results[i].duration;
}
avgDuration = avgDuration / this.results.length;
gs.info('Performance Test Results:');
gs.info('Average Duration: ' + avgDuration + 'ms');
gs.info('Total Tests: ' + this.results.length);
},
type: 'PerformanceTest'
};
Best Practices Summary
Daily Monitoring Checklist
- Review Performance Dashboard: Check response times and error rates
- Monitor Resource Usage: CPU, memory, database connections
- Check Slow Queries: Review database performance logs
- Validate Scheduled Jobs: Ensure jobs complete within expected timeframes
- User Experience Metrics: Monitor support ticket trends
Weekly Deep Dive
- Performance Trend Analysis: Week-over-week performance comparison
- Capacity Planning: Project resource needs based on growth trends
- Script Performance Review: Identify optimization opportunities
- Cache Hit Rate Analysis: Optimize caching strategies
- System Health Report: Comprehensive instance health assessment
Monthly Optimization
- Database Maintenance: Index optimization and cleanup
- Performance Baseline Updates: Adjust thresholds based on new patterns
- Capacity Scaling Review: Plan infrastructure adjustments
- Business Rule Audit: Remove unnecessary or redundant rules
- Performance Policy Updates: Refine monitoring and alerting rules
Conclusion
Effective ServiceNow performance monitoring requires a combination of proactive monitoring, regular optimization, and strategic planning. By implementing these monitoring strategies and optimization techniques, you can maintain optimal performance even as your instance grows and evolves.
Remember that performance optimization is an ongoing process. Regular monitoring, timely interventions, and continuous improvement are key to maintaining a high-performing ServiceNow instance that supports your organization's business objectives.
Start with the basic monitoring tools, implement the optimization strategies that align with your current challenges, and gradually build a comprehensive performance management framework that ensures your ServiceNow instance delivers consistent, reliable performance for all users.
