Report Scheduling Guide
Table of Contents
- Introduction
- Creating Schedulers
- Scheduler Types
- Cron Expression Syntax
- Scheduler Parameters
- Managing Schedulers
- Testing and Debugging
- Best Practices
Introduction
Schedulers automate the execution of reports, workers, and reminders based on recurring schedules (cron expressions) or one-time dates. This guide covers how to create, configure, and manage schedulers in the Syneo/Barcoding system.
Creating Schedulers
Via Web Application
Schedulers are typically created through the web application interface:
- Navigate to the Scheduler management page
- Click "Create New Scheduler"
- Fill in the required fields:
- Description: Human-readable description
- Type: Report, Worker, or Reminder
- Schedule Type: Recurring (cron) or One-time (date)
- Schedule: Cron expression or date/time
- Parameters: Task-specific parameters (JSON)
- Status: Active/Inactive
Via API
Create a recurring scheduler:
POST /schedulers
Content-Type: application/json
{
"description": "Daily sales report at 8 AM",
"report_id": 25,
"schedule": "0 8 * * *",
"status": true,
"params": {
"controller": "RefreshUserToken",
"options_interface": {
"delivery": {
"email": true,
"instant": false
},
"output": {
"file": {
"type": "Direct",
"format": "PDF"
}
}
}
},
"user_id_created": 1
}Create a one-time scheduler:
POST /schedulers
Content-Type: application/json
{
"description": "Send reminder on December 15th at 3 PM",
"reminder_id": 10,
"date_time": "2025-12-15T15:00:00.000Z",
"status": true,
"params": {
"message": "Your appointment is tomorrow"
},
"user_id_created": 1
}Via Database
Directly insert into the Scheduler table:
INSERT INTO Scheduler (
description,
report_id,
schedule,
status,
params,
user_id_created
) VALUES (
'Daily token refresh',
25,
'0 8 * * *',
1,
'{"controller": "RefreshUserToken", "options_interface": {...}}',
1
);Scheduler Types
1. Report Schedulers
Execute reports automatically based on schedule.
Example: Daily Sales Report
{
"description": "Daily sales report at 8 AM",
"report_id": 25,
"schedule": "0 8 * * *",
"status": true,
"params": {
"controller": "SalesReport",
"options_interface": {
"delivery": {
"email": true,
"instant": false,
"deliver_to": {
"user_id": [1, 2, 3]
}
},
"output": {
"file": {
"type": "Direct",
"format": "PDF"
}
}
},
"report_params": {
"start_date": "2025-12-01",
"end_date": "2025-12-31"
}
}
}Example: Hourly Token Refresh
{
"description": "Refresh user tokens every hour",
"report_id": 30,
"schedule": "0 * * * *",
"status": true,
"params": {
"controller": "RefreshUserToken",
"options_interface": {
"delivery": {
"instant": false
}
}
}
}2. Worker Schedulers
Execute workers (background jobs) based on schedule.
Example: Data Sync Worker
{
"description": "Sync external data every 30 minutes",
"worker_id": 5,
"schedule": "*/30 * * * *",
"status": true,
"params": {
"source": "external_api",
"batch_size": 1000
}
}3. Reminder Schedulers
Send reminders at scheduled times.
Example: One-Time Meeting Reminder
{
"description": "Meeting reminder for December 15th",
"reminder_id": 10,
"date_time": "2025-12-15T14:00:00.000Z",
"status": true,
"params": {
"message": "Team meeting starts in 1 hour",
"recipients": [1, 2, 3, 4, 5]
}
}4. Stay-Alive Schedulers
Special type for long-running, continuously active processes.
Characteristics:
stay_alive: trueflag set- Not processed by cron-scheduler (runs in standalone container)
- Automatically restarts if it crashes
- Used for services that need to be always running
Example: Real-time Monitoring Worker
{
"description": "Real-time system monitor",
"worker_id": 15,
"stay_alive": true,
"status": true
}Note: Stay-alive schedulers are excluded from cron-scheduler processing:
where: {
status: true,
stay_alive: false // Exclude stay-alive schedulers
}Scheduler Types
Recurring Schedulers (Cron Expression)
Uses the schedule field with cron syntax.
Database Field:
schedule: string // e.g., "0 8 * * *"Execution:
- Evaluated every minute by cron-scheduler
- Executes when current time matches cron expression
- Continues executing on schedule until disabled
Use Cases:
- Daily, weekly, monthly reports
- Periodic data synchronization
- Regular maintenance tasks
One-Time Schedulers (Date/Time)
Uses the date_time field with ISO 8601 date string.
Database Field:
date_time: Date // e.g., "2025-12-15T15:00:00.000Z"Execution:
- Evaluated every minute by cron-scheduler
- Executes when current time matches scheduled time (within the same minute)
- Automatically disabled after execution
Use Cases:
- One-time reminders
- Scheduled future tasks
- Time-specific actions
Cron Expression Syntax
Cron expressions define recurring schedules using 5 fields:
* * * * *
│ │ │ │ │
│ │ │ │ └─── Day of week (0-6, Sunday=0)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of month (1-31)
│ └───────── Hour (0-23)
└─────────── Minute (0-59)Syntax Elements
| Symbol | Meaning | Example |
|---|---|---|
* | Any value | * * * * * = Every minute |
5 | Specific value | 5 * * * * = At minute 5 of every hour |
1-5 | Range | 0 8-17 * * * = Every hour from 8 AM to 5 PM |
*/5 | Step values | */5 * * * * = Every 5 minutes |
1,3,5 | List | 0 8 * * 1,3,5 = 8 AM on Mon, Wed, Fri |
Common Examples
Every Minute:
* * * * *Every Hour:
0 * * * *Every Day at 8:00 AM:
0 8 * * *Every Hour from 9 AM to 5 PM:
0 9-17 * * *Every 5 Minutes:
*/5 * * * *Every 30 Minutes:
*/30 * * * *or
0,30 * * * *Every Monday at 9:00 AM:
0 9 * * 1Every Weekday at 6:00 PM:
0 18 * * 1-5First Day of Month at Midnight:
0 0 1 * *Every Quarter (Jan, Apr, Jul, Oct) on 1st at Midnight:
0 0 1 1,4,7,10 *Every 2 Hours:
0 */2 * * *Every 15 Minutes During Business Hours (9 AM - 5 PM):
*/15 9-17 * * *Weekend Days at 10:00 AM:
0 10 * * 0,6Validation
The cron-scheduler validates expressions before evaluating:
if (!cron.validate(cronExpression)) {
console.error(`Invalid cron expression: ${cronExpression}`);
return false;
}Invalid expressions will be logged and skipped.
Testing Cron Expressions
Use online tools to test cron expressions:
Or test in Node.js:
const cron = require('node-cron');
const expression = '0 8 * * *';
console.log(cron.validate(expression)); // true or false
// Test against specific date
const task = cron.schedule(expression, () => {
console.log('Task would execute now!');
}, {
scheduled: false
});Scheduler Parameters
The params field stores task-specific configuration as JSON.
Report Parameters
{
"controller": "SalesReport",
"options_interface": {
"delivery": {
"email": true,
"instant": false,
"deliver_to": {
"user_id": [1, 2, 3],
"group_id": [5]
}
},
"output": {
"file": {
"type": "Direct",
"format": "PDF",
"filename": "sales_report_{{date}}.pdf"
}
}
},
"report_params": {
"start_date": "2025-12-01",
"end_date": "2025-12-31",
"include_charts": true
}
}Key Fields:
controller: Report class name (e.g., "SalesReport")options_interface.delivery:email: Send via email (boolean)instant: Execute in foreground (boolean) - forced to true in cron contextdeliver_to: User/group IDs to deliver to
options_interface.output.file:type: "Direct" or "Attachment"format: "PDF", "Excel", "CSV", etc.filename: Output filename (supports templates)
report_params: Report-specific parameters
Worker Parameters
{
"source": "external_api",
"batch_size": 1000,
"timeout": 30000,
"retry_on_failure": true
}Worker parameters are passed as command-line arguments:
// Scheduler params: {"source": "external_api", "batch_size": 1000}
// Becomes: ["source=external_api", "batch_size=1000", "scheduler_id=16"]Dynamic Parameters
Parameters can include dynamic values using templates:
{
"filename": "report_{{date}}.pdf",
"date_range": {
"start": "{{yesterday}}",
"end": "{{today}}"
}
}Template Variables (if implemented in report):
: Current date: Yesterday's date: Today's date: Current user ID
Managing Schedulers
Enable/Disable Scheduler
Via API:
PATCH /schedulers/16
Content-Type: application/json
{
"status": false // Disable scheduler
}Via Database:
UPDATE Scheduler SET status = 0 WHERE id = 16;Effect: Disabled schedulers are not evaluated by cron-scheduler.
Update Schedule
PATCH /schedulers/16
Content-Type: application/json
{
"schedule": "0 9 * * *" // Change from 8 AM to 9 AM
}Effect: Takes effect on next cron-scheduler evaluation (within 1 minute).
Update Parameters
PATCH /schedulers/16
Content-Type: application/json
{
"params": {
"controller": "SalesReport",
"report_params": {
"include_charts": false // Disable charts
}
}
}Delete Scheduler
DELETE /schedulers/16Warning: This permanently deletes the scheduler. Consider disabling (status: false) instead.
View Execution History
Query the SchedulerExecution table:
SELECT
se.id,
se.execution_key,
se.executed_at,
se.container_id,
s.description
FROM SchedulerExecution se
JOIN Scheduler s ON s.id = se.scheduler_id
WHERE se.scheduler_id = 16
ORDER BY se.executed_at DESC
LIMIT 10;Example Result:
id | execution_key | executed_at | container_id | description
----|----------------------|--------------------------|-------------------|------------------
156 | cron-2025-12-12-8-0 | 2025-12-12 08:00:00.123 | hostname-12345 | Daily token refresh
155 | cron-2025-12-11-8-0 | 2025-12-11 08:00:00.456 | hostname-12345 | Daily token refresh
154 | cron-2025-12-10-8-0 | 2025-12-10 08:00:00.789 | hostname-67890 | Daily token refreshCheck Failed Executions
Query the RabbitMQ failed queue:
docker exec rabbitmq rabbitmqadmin get queue=cron_scheduler_failed count=10Or check worker logs:
grep "PERMANENTLY FAILED" cron-worker.logTesting and Debugging
Test Scheduler Immediately
Option 1: Set date_time to current time
UPDATE Scheduler
SET date_time = NOW() + INTERVAL 1 MINUTE
WHERE id = 16;Wait 1 minute for execution.
Option 2: Manually publish to queue
# Publish message directly to RabbitMQ
docker exec rabbitmq rabbitmqadmin publish \
exchange=amq.default \
routing_key=cron_scheduler \
payload='{"type":"report","id":25,"scheduler_id":16,"params_scheduler":"{}"}'Debug Scheduler Not Executing
Check 1: Is scheduler active?
SELECT id, description, status, schedule, date_time
FROM Scheduler
WHERE id = 16;Expected: status = 1
Check 2: Is cron expression valid?
const cron = require('node-cron');
console.log(cron.validate('0 8 * * *')); // Should be trueCheck 3: Does cron expression match current time?
# Check current time
date
# Expected format: Fri Dec 12 08:00:00 UTC 2025
# If schedule is "0 8 * * *", it should execute at 08:00:00Check 4: Was execution already claimed?
SELECT * FROM SchedulerExecution
WHERE scheduler_id = 16
AND DATE(executed_at) = CURDATE()
ORDER BY executed_at DESC;If record exists with today's date and matching execution_key, it was already executed.
Check 5: Is cron-scheduler running?
ps aux | grep cron-schedulerCheck 6: Check cron-scheduler logs
tail -f cron-scheduler.log
# Look for:
# ✓ Claimed scheduler 16: Daily token refresh
# ✓ Published: report 25Debug Task Execution Failures
Check 1: Worker logs
tail -f cron-worker.log
# Look for:
# [ERROR] [type=report, id=25, scheduler_id=16] Task execution failed
# [ERROR] Stack trace: ...Check 2: Check retry count
grep "retry" cron-worker.log | grep "scheduler_id=16"
# Look for:
# [WARN] [scheduler_id=16] Scheduling retry 1/3 in 30000ms
# [WARN] [scheduler_id=16] Scheduling retry 2/3 in 60000msCheck 3: Check failed queue
docker exec rabbitmq rabbitmqadmin list queues name messages
# Look for non-zero message count in cron_scheduler_failedCheck 4: Inspect failed message
docker exec rabbitmq rabbitmqadmin get queue=cron_scheduler_failed
# Output includes:
# - payload: Task details
# - failed_reason: Error message
# - retry_count: Number of retries attemptedMonitor Scheduler Performance
Check execution duration:
grep "duration" cron-worker.log | grep "scheduler_id=16"
# Example output:
# 2025-12-12T08:00:05.123Z [SUCCESS] [scheduler_id=16, duration=5123ms] Task completedCheck queue depth:
docker exec rabbitmq rabbitmqadmin list queues name messages
# Output:
# cron_scheduler 5 (5 tasks pending)
# cron_scheduler_retry 2 (2 tasks waiting for retry)
# cron_scheduler_failed 1 (1 permanently failed task)Best Practices
1. Use Descriptive Scheduler Names
{
"description": "Daily sales report at 8 AM (Mon-Fri)",
// Not: "Report 1"
}2. Set Appropriate Schedules
Frequent reports: Avoid overlapping executions
❌ BAD: */5 * * * * (every 5 minutes) for a 10-minute report ✅ GOOD: */15 * * * * (every 15 minutes) for a 10-minute reportResource-intensive tasks: Schedule during off-peak hours
✅ GOOD: 0 2 * * * (2 AM) for heavy database queries
3. Design Idempotent Tasks
Tasks should produce the same result when executed multiple times:
// ❌ BAD: Not idempotent
async mainFunction() {
await this.db.query('UPDATE counters SET value = value + 1');
}
// ✅ GOOD: Idempotent
async mainFunction() {
const date = new Date().toISOString().split('T')[0];
await this.db.query(
'INSERT INTO daily_stats (date, value) VALUES (?, 1) ' +
'ON DUPLICATE KEY UPDATE value = 1',
[date]
);
}4. Handle Transient vs Permanent Errors
async mainFunction() {
try {
await this.externalAPI.fetch();
} catch (err) {
if (err.code === 'ECONNREFUSED' || err.code === 'ETIMEDOUT') {
// Transient error - throw to trigger retry
throw err;
} else if (err.code === 'AUTH_INVALID') {
// Permanent error - log and don't throw
await this.log(LogType.Error, 'Invalid credentials - manual intervention required');
return; // Don't throw - retrying won't help
}
}
}5. Use Appropriate Notification Methods
{
"params": {
"options_interface": {
"delivery": {
"email": true, // For reports to be reviewed
"web_notification": true, // For quick alerts
"instant": false // Let it run in background (forced to true by worker)
}
}
}
}6. Test Before Enabling
- Create scheduler with
status: false - Manually test execution
- Verify output/logs
- Enable with
status: true
7. Monitor Failed Executions
Set up alerts for failed tasks:
# Check failed queue daily
#!/bin/bash
failed_count=$(docker exec rabbitmq rabbitmqadmin list queues -f tsv | grep cron_scheduler_failed | awk '{print $2}')
if [ "$failed_count" -gt 0 ]; then
echo "ALERT: $failed_count failed tasks in queue!"
# Send notification
fi8. Cleanup Old Schedulers
Regularly review and disable/delete unused schedulers:
-- Find schedulers not executed in 30 days
SELECT s.id, s.description, MAX(se.executed_at) as last_execution
FROM Scheduler s
LEFT JOIN SchedulerExecution se ON s.id = se.scheduler_id
WHERE s.status = 1
GROUP BY s.id
HAVING last_execution IS NULL OR last_execution < DATE_SUB(NOW(), INTERVAL 30 DAY);9. Document Parameters
Add comments to complex parameter configurations:
{
"description": "Monthly sales report with regional breakdown",
"params": {
"controller": "SalesReport",
"report_params": {
"aggregation": "monthly", // Groups data by month
"regions": ["NA", "EU", "APAC"], // North America, Europe, Asia-Pacific
"include_forecast": true // Includes predictive analytics
}
}
}10. Version Control Scheduler Definitions
Store scheduler configurations in version control:
// schedulers/daily-sales-report.json
{
"description": "Daily sales report at 8 AM",
"report_id": 25,
"schedule": "0 8 * * *",
"status": true,
"params": { /* ... */ }
}Import using scripts:
#!/bin/bash
for file in schedulers/*.json; do
curl -X POST http://localhost:3001/schedulers \
-H "Content-Type: application/json" \
-d @"$file"
doneRelated Documentation
- Cron Worker System - System architecture overview
- Retry Mechanism - How retries work