scheduled-jobs

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Scheduled Jobs for ServiceNow

ServiceNow 定时任务

Scheduled Jobs automate recurring tasks, batch processing, and maintenance operations.
定时任务可实现周期性任务、批处理和维护操作的自动化。

Job Types

任务类型

TypeTablePurpose
Scheduled Script Executionsysauto_scriptRun custom scripts
Report Schedulersysauto_reportGenerate and email reports
Table Cleanersys_auto_flushDelete old records
LDAP Refreshldap_server_configSync LDAP data
Discoverydiscovery_scheduleNetwork discovery
类型表名用途
定时脚本执行sysauto_script运行自定义脚本
报表调度器sysauto_report生成并发送报表邮件
表清理器sys_auto_flush删除旧记录
LDAP刷新ldap_server_config同步LDAP数据
发现任务discovery_schedule网络发现

Scheduled Script Execution (ES5)

定时脚本执行(ES5)

Basic Scheduled Job

基础定时任务

javascript
// Table: sysauto_script
// Name: Close Stale Incidents
// Run: Daily at 2:00 AM

// Script (ES5 ONLY!):
(function executeScheduledJob() {
    var LOG_PREFIX = '[CloseStaleIncidents] ';
    var closedCount = 0;

    // Find incidents inactive for 30 days
    var staleDate = new GlideDateTime();
    staleDate.addDaysLocalTime(-30);

    var gr = new GlideRecord('incident');
    gr.addQuery('state', 'IN', '1,2,3');  // New, In Progress, On Hold
    gr.addQuery('sys_updated_on', '<', staleDate);
    gr.addQuery('active', true);
    gr.query();

    gs.info(LOG_PREFIX + 'Found ' + gr.getRowCount() + ' stale incidents');

    while (gr.next()) {
        gr.state = 7;  // Closed
        gr.close_code = 'Closed/Resolved by Caller';
        gr.close_notes = 'Auto-closed due to 30 days of inactivity';
        gr.update();
        closedCount++;
    }

    gs.info(LOG_PREFIX + 'Closed ' + closedCount + ' stale incidents');
})();
javascript
// Table: sysauto_script
// Name: Close Stale Incidents
// Run: Daily at 2:00 AM

// Script (ES5 ONLY!):
(function executeScheduledJob() {
    var LOG_PREFIX = '[CloseStaleIncidents] ';
    var closedCount = 0;

    // Find incidents inactive for 30 days
    var staleDate = new GlideDateTime();
    staleDate.addDaysLocalTime(-30);

    var gr = new GlideRecord('incident');
    gr.addQuery('state', 'IN', '1,2,3');  // New, In Progress, On Hold
    gr.addQuery('sys_updated_on', '<', staleDate);
    gr.addQuery('active', true);
    gr.query();

    gs.info(LOG_PREFIX + 'Found ' + gr.getRowCount() + ' stale incidents');

    while (gr.next()) {
        gr.state = 7;  // Closed
        gr.close_code = 'Closed/Resolved by Caller';
        gr.close_notes = 'Auto-closed due to 30 days of inactivity';
        gr.update();
        closedCount++;
    }

    gs.info(LOG_PREFIX + 'Closed ' + closedCount + ' stale incidents');
})();

Scheduled Job with Error Handling (ES5)

带错误处理的定时任务(ES5)

javascript
// Name: Sync User Data
// Run: Every 6 hours

(function executeScheduledJob() {
    var LOG_PREFIX = '[SyncUserData] ';
    var stats = {
        processed: 0,
        updated: 0,
        errors: 0
    };

    try {
        // Get users needing sync
        var gr = new GlideRecord('sys_user');
        gr.addQuery('u_needs_sync', true);
        gr.addQuery('active', true);
        gr.setLimit(1000);  // Process in batches
        gr.query();

        while (gr.next()) {
            stats.processed++;
            try {
                var updated = syncUserFromSource(gr);
                if (updated) {
                    stats.updated++;
                }
            } catch (e) {
                stats.errors++;
                gs.error(LOG_PREFIX + 'Error syncing user ' + gr.user_name + ': ' + e.message);
            }
        }

        gs.info(LOG_PREFIX + 'Sync complete: ' + JSON.stringify(stats));

        // Send summary email if errors
        if (stats.errors > 0) {
            sendErrorSummary(stats);
        }

    } catch (e) {
        gs.error(LOG_PREFIX + 'Job failed: ' + e.message);
        notifyAdmins('User sync job failed: ' + e.message);
    }

    function syncUserFromSource(userGr) {
        // Sync logic here
        userGr.u_needs_sync = false;
        userGr.u_last_sync = new GlideDateTime();
        return userGr.update();
    }

    function sendErrorSummary(stats) {
        gs.eventQueue('user.sync.errors', null, JSON.stringify(stats), '');
    }

    function notifyAdmins(message) {
        gs.eventQueue('system.job.failure', null, message, '');
    }
})();
javascript
// Name: Sync User Data
// Run: Every 6 hours

(function executeScheduledJob() {
    var LOG_PREFIX = '[SyncUserData] ';
    var stats = {
        processed: 0,
        updated: 0,
        errors: 0
    };

    try {
        // Get users needing sync
        var gr = new GlideRecord('sys_user');
        gr.addQuery('u_needs_sync', true);
        gr.addQuery('active', true);
        gr.setLimit(1000);  // Process in batches
        gr.query();

        while (gr.next()) {
            stats.processed++;
            try {
                var updated = syncUserFromSource(gr);
                if (updated) {
                    stats.updated++;
                }
            } catch (e) {
                stats.errors++;
                gs.error(LOG_PREFIX + 'Error syncing user ' + gr.user_name + ': ' + e.message);
            }
        }

        gs.info(LOG_PREFIX + 'Sync complete: ' + JSON.stringify(stats));

        // Send summary email if errors
        if (stats.errors > 0) {
            sendErrorSummary(stats);
        }

    } catch (e) {
        gs.error(LOG_PREFIX + 'Job failed: ' + e.message);
        notifyAdmins('User sync job failed: ' + e.message);
    }

    function syncUserFromSource(userGr) {
        // Sync logic here
        userGr.u_needs_sync = false;
        userGr.u_last_sync = new GlideDateTime();
        return userGr.update();
    }

    function sendErrorSummary(stats) {
        gs.eventQueue('user.sync.errors', null, JSON.stringify(stats), '');
    }

    function notifyAdmins(message) {
        gs.eventQueue('system.job.failure', null, message, '');
    }
})();

Batch Processing Job (ES5)

批处理任务(ES5)

javascript
// Name: Process Large Dataset
// Run: Weekly on Sunday at 1:00 AM

(function executeScheduledJob() {
    var LOG_PREFIX = '[BatchProcessor] ';
    var BATCH_SIZE = 500;
    var MAX_RUNTIME = 3600000;  // 1 hour in ms
    var startTime = new Date().getTime();

    var processed = 0;
    var hasMore = true;

    while (hasMore && !isTimeExceeded()) {
        hasMore = processBatch();
    }

    if (hasMore) {
        gs.warn(LOG_PREFIX + 'Job stopped due to time limit. Processed: ' + processed);
        // Re-queue for next run
        queueContinuation();
    } else {
        gs.info(LOG_PREFIX + 'Job complete. Total processed: ' + processed);
    }

    function processBatch() {
        var gr = new GlideRecord('u_large_table');
        gr.addQuery('u_processed', false);
        gr.setLimit(BATCH_SIZE);
        gr.query();

        if (!gr.hasNext()) {
            return false;
        }

        while (gr.next()) {
            processRecord(gr);
            processed++;
        }

        return true;
    }

    function processRecord(gr) {
        // Processing logic
        gr.u_processed = true;
        gr.u_processed_date = new GlideDateTime();
        gr.update();
    }

    function isTimeExceeded() {
        var elapsed = new Date().getTime() - startTime;
        return elapsed > MAX_RUNTIME;
    }

    function queueContinuation() {
        // Queue another run
        var job = new GlideRecord('sysauto_script');
        if (job.get('name', 'Process Large Dataset - Continuation')) {
            job.next_action = new GlideDateTime();
            job.update();
        }
    }
})();
javascript
// Name: Process Large Dataset
// Run: Weekly on Sunday at 1:00 AM

(function executeScheduledJob() {
    var LOG_PREFIX = '[BatchProcessor] ';
    var BATCH_SIZE = 500;
    var MAX_RUNTIME = 3600000;  // 1 hour in ms
    var startTime = new Date().getTime();

    var processed = 0;
    var hasMore = true;

    while (hasMore && !isTimeExceeded()) {
        hasMore = processBatch();
    }

    if (hasMore) {
        gs.warn(LOG_PREFIX + 'Job stopped due to time limit. Processed: ' + processed);
        // Re-queue for next run
        queueContinuation();
    } else {
        gs.info(LOG_PREFIX + 'Job complete. Total processed: ' + processed);
    }

    function processBatch() {
        var gr = new GlideRecord('u_large_table');
        gr.addQuery('u_processed', false);
        gr.setLimit(BATCH_SIZE);
        gr.query();

        if (!gr.hasNext()) {
            return false;
        }

        while (gr.next()) {
            processRecord(gr);
            processed++;
        }

        return true;
    }

    function processRecord(gr) {
        // Processing logic
        gr.u_processed = true;
        gr.u_processed_date = new GlideDateTime();
        gr.update();
    }

    function isTimeExceeded() {
        var elapsed = new Date().getTime() - startTime;
        return elapsed > MAX_RUNTIME;
    }

    function queueContinuation() {
        // Queue another run
        var job = new GlideRecord('sysauto_script');
        if (job.get('name', 'Process Large Dataset - Continuation')) {
            job.next_action = new GlideDateTime();
            job.update();
        }
    }
})();

Schedule Configuration

调度配置

Run Frequencies

运行频率

FrequencyCronExample
Every 5 minutes
0 */5 * * * ?
Health checks
Hourly
0 0 * * * ?
Data sync
Daily at midnight
0 0 0 * * ?
Cleanup
Weekly Sunday
0 0 0 ? * SUN
Reports
Monthly 1st
0 0 0 1 * ?
Billing
CustomVariousSpecific needs
频率Cron表达式示例
每5分钟一次
0 */5 * * * ?
健康检查
每小时一次
0 0 * * * ?
数据同步
每日午夜
0 0 0 * * ?
清理操作
每周日
0 0 0 ? * SUN
报表生成
每月1号
0 0 0 1 * ?
账单处理
自定义多种表达式特定需求

Create Scheduled Job (ES5)

创建定时任务(ES5)

javascript
// Create scheduled job programmatically (ES5 ONLY!)
var job = new GlideRecord('sysauto_script');
job.initialize();
job.setValue('name', 'Nightly Cleanup');
job.setValue('active', true);

// Schedule: Daily at 2:00 AM
job.setValue('run_type', 'daily');
job.setValue('run_time', '02:00:00');
// Or use explicit schedule
job.setValue('run_dayofweek', 'daily');

// Script
job.setValue('script',
    '(function executeScheduledJob() {\n' +
    '    var gr = new GlideRecord("sys_audit_delete");\n' +
    '    gr.addQuery("sys_created_on", "<", gs.daysAgo(90));\n' +
    '    gr.deleteMultiple();\n' +
    '    gs.info("Cleanup complete");\n' +
    '})();'
);

// Run as system
job.setValue('run_as', '');  // Empty = System

job.insert();
javascript
// Create scheduled job programmatically (ES5 ONLY!)
var job = new GlideRecord('sysauto_script');
job.initialize();
job.setValue('name', 'Nightly Cleanup');
job.setValue('active', true);

// Schedule: Daily at 2:00 AM
job.setValue('run_type', 'daily');
job.setValue('run_time', '02:00:00');
// Or use explicit schedule
job.setValue('run_dayofweek', 'daily');

// Script
job.setValue('script',
    '(function executeScheduledJob() {\n' +
    '    var gr = new GlideRecord("sys_audit_delete");\n' +
    '    gr.addQuery("sys_created_on", "<", gs.daysAgo(90));\n' +
    '    gr.deleteMultiple();\n' +
    '    gs.info("Cleanup complete");\n' +
    '})();'
);

// Run as system
job.setValue('run_as', '');  // Empty = System

job.insert();

Conditional Execution

条件执行

javascript
// Job that checks conditions before running (ES5 ONLY!)
(function executeScheduledJob() {
    var LOG_PREFIX = '[ConditionalJob] ';

    // Check if job should run
    if (!shouldRun()) {
        gs.info(LOG_PREFIX + 'Skipping execution - conditions not met');
        return;
    }

    // Execute main logic
    executeMainTask();

    function shouldRun() {
        // Check business hours
        var now = new GlideDateTime();
        var hour = parseInt(now.getLocalTime().getByFormat('HH'), 10);

        // Only run outside business hours (before 6am or after 8pm)
        if (hour >= 6 && hour < 20) {
            return false;
        }

        // Check for active change freeze
        var freeze = new GlideRecord('change_request');
        freeze.addQuery('type', 'freeze');
        freeze.addQuery('state', 'implement');
        freeze.query();

        if (freeze.hasNext()) {
            gs.info(LOG_PREFIX + 'Change freeze active');
            return false;
        }

        return true;
    }

    function executeMainTask() {
        // Main job logic here
        gs.info(LOG_PREFIX + 'Executing main task');
    }
})();
javascript
// Job that checks conditions before running (ES5 ONLY!)
(function executeScheduledJob() {
    var LOG_PREFIX = '[ConditionalJob] ';

    // Check if job should run
    if (!shouldRun()) {
        gs.info(LOG_PREFIX + 'Skipping execution - conditions not met');
        return;
    }

    // Execute main logic
    executeMainTask();

    function shouldRun() {
        // Check business hours
        var now = new GlideDateTime();
        var hour = parseInt(now.getLocalTime().getByFormat('HH'), 10);

        // Only run outside business hours (before 6am or after 8pm)
        if (hour >= 6 && hour < 20) {
            return false;
        }

        // Check for active change freeze
        var freeze = new GlideRecord('change_request');
        freeze.addQuery('type', 'freeze');
        freeze.addQuery('state', 'implement');
        freeze.query();

        if (freeze.hasNext()) {
            gs.info(LOG_PREFIX + 'Change freeze active');
            return false;
        }

        return true;
    }

    function executeMainTask() {
        // Main job logic here
        gs.info(LOG_PREFIX + 'Executing main task');
    }
})();

Job Monitoring

任务监控

Check Job Status (ES5)

检查任务状态(ES5)

javascript
// Query scheduled job history (ES5 ONLY!)
var history = new GlideRecord('sys_trigger');
history.addQuery('name', 'CONTAINS', 'Nightly Cleanup');
history.orderByDesc('sys_created_on');
history.setLimit(10);
history.query();

while (history.next()) {
    gs.info('Job: ' + history.getValue('name') +
            ' | State: ' + history.getValue('state') +
            ' | Next: ' + history.getValue('next_action'));
}
javascript
// Query scheduled job history (ES5 ONLY!)
var history = new GlideRecord('sys_trigger');
history.addQuery('name', 'CONTAINS', 'Nightly Cleanup');
history.orderByDesc('sys_created_on');
history.setLimit(10);
history.query();

while (history.next()) {
    gs.info('Job: ' + history.getValue('name') +
            ' | State: ' + history.getValue('state') +
            ' | Next: ' + history.getValue('next_action'));
}

Job with Metrics (ES5)

带指标统计的任务(ES5)

javascript
// Job that records performance metrics (ES5 ONLY!)
(function executeScheduledJob() {
    var LOG_PREFIX = '[MetricsJob] ';
    var startTime = new Date().getTime();
    var metrics = {
        startTime: new GlideDateTime().getDisplayValue(),
        recordsProcessed: 0,
        errors: 0
    };

    try {
        // Main processing
        var gr = new GlideRecord('incident');
        gr.addQuery('active', true);
        gr.query();

        while (gr.next()) {
            processRecord(gr);
            metrics.recordsProcessed++;
        }

    } catch (e) {
        metrics.errors++;
        gs.error(LOG_PREFIX + 'Error: ' + e.message);
    } finally {
        // Record metrics
        metrics.endTime = new GlideDateTime().getDisplayValue();
        metrics.duration = (new Date().getTime() - startTime) / 1000;
        recordMetrics(metrics);
    }

    function processRecord(gr) {
        // Processing logic
    }

    function recordMetrics(metrics) {
        var metricsRecord = new GlideRecord('u_job_metrics');
        metricsRecord.initialize();
        metricsRecord.setValue('u_job_name', 'MetricsJob');
        metricsRecord.setValue('u_start_time', metrics.startTime);
        metricsRecord.setValue('u_end_time', metrics.endTime);
        metricsRecord.setValue('u_duration', metrics.duration);
        metricsRecord.setValue('u_records_processed', metrics.recordsProcessed);
        metricsRecord.setValue('u_errors', metrics.errors);
        metricsRecord.insert();

        gs.info(LOG_PREFIX + 'Metrics: ' + JSON.stringify(metrics));
    }
})();
javascript
// Job that records performance metrics (ES5 ONLY!)
(function executeScheduledJob() {
    var LOG_PREFIX = '[MetricsJob] ';
    var startTime = new Date().getTime();
    var metrics = {
        startTime: new GlideDateTime().getDisplayValue(),
        recordsProcessed: 0,
        errors: 0
    };

    try {
        // Main processing
        var gr = new GlideRecord('incident');
        gr.addQuery('active', true);
        gr.query();

        while (gr.next()) {
            processRecord(gr);
            metrics.recordsProcessed++;
        }

    } catch (e) {
        metrics.errors++;
        gs.error(LOG_PREFIX + 'Error: ' + e.message);
    } finally {
        // Record metrics
        metrics.endTime = new GlideDateTime().getDisplayValue();
        metrics.duration = (new Date().getTime() - startTime) / 1000;
        recordMetrics(metrics);
    }

    function processRecord(gr) {
        // Processing logic
    }

    function recordMetrics(metrics) {
        var metricsRecord = new GlideRecord('u_job_metrics');
        metricsRecord.initialize();
        metricsRecord.setValue('u_job_name', 'MetricsJob');
        metricsRecord.setValue('u_start_time', metrics.startTime);
        metricsRecord.setValue('u_end_time', metrics.endTime);
        metricsRecord.setValue('u_duration', metrics.duration);
        metricsRecord.setValue('u_records_processed', metrics.recordsProcessed);
        metricsRecord.setValue('u_errors', metrics.errors);
        metricsRecord.insert();

        gs.info(LOG_PREFIX + 'Metrics: ' + JSON.stringify(metrics));
    }
})();

MCP Tool Integration

MCP工具集成

Available Tools

可用工具

ToolPurpose
snow_schedule_job
Create scheduled job
snow_find_artifact
Find existing jobs
snow_execute_script_with_output
Test job script
snow_get_logs
Check job execution logs
工具用途
snow_schedule_job
创建定时任务
snow_find_artifact
查找现有任务
snow_execute_script_with_output
测试任务脚本
snow_get_logs
查看任务执行日志

Example Workflow

示例工作流

javascript
// 1. Create scheduled job
await snow_schedule_job({
    name: 'Daily Report Generator',
    run_type: 'daily',
    run_time: '06:00:00',
    script: '/* report generation script */',
    active: true
});

// 2. Test the script
await snow_execute_script_with_output({
    script: '/* test job script */'
});

// 3. Check logs
await snow_get_logs({
    filter: 'message CONTAINS "Daily Report"',
    limit: 50
});
javascript
// 1. Create scheduled job
await snow_schedule_job({
    name: 'Daily Report Generator',
    run_type: 'daily',
    run_time: '06:00:00',
    script: '/* report generation script */',
    active: true
});

// 2. Test the script
await snow_execute_script_with_output({
    script: '/* test job script */'
});

// 3. Check logs
await snow_get_logs({
    filter: 'message CONTAINS "Daily Report"',
    limit: 50
});

Best Practices

最佳实践

  1. Logging - Comprehensive logging for debugging
  2. Error Handling - Try-catch with notifications
  3. Batching - Process large datasets in batches
  4. Time Limits - Check runtime to prevent timeouts
  5. Off-Peak - Schedule during low-usage periods
  6. Idempotent - Safe to run multiple times
  7. Monitoring - Record metrics and status
  8. ES5 Only - No modern JavaScript syntax
  1. 日志记录 - 完善的日志便于调试
  2. 错误处理 - 使用Try-Catch并配置通知
  3. 批处理 - 分批次处理大型数据集
  4. 时间限制 - 检查运行时长以避免超时
  5. 非高峰时段 - 在低负载时段调度任务
  6. 幂等性 - 可安全重复执行
  7. 监控 - 记录指标和状态
  8. 仅支持ES5 - 不使用现代JavaScript语法