Scheduled Jobs
The SALLY backend uses two scheduling mechanisms:
- BullMQ repeatable jobs (
@nestjs/bullmq) — for integration sync jobs that need concurrency control, retry, and visibility via Bull Board - NestJS Schedule (
@nestjs/schedule) — for lightweight cron tasks like alert escalation and digest emails
Redis is required for BullMQ. The queue infrastructure is registered globally in QueueModule.
Quick Reference
| Job | Frequency | Mechanism | Domain | Status |
|---|---|---|---|---|
| Drivers Sync | Every 15 min | Bull repeatable | Integrations | Active |
| Vehicles Sync | Every 15 min | Bull repeatable | Integrations | Active |
| Loads Sync | Every 15 min | Bull repeatable | Integrations | Active |
| HOS Sync | Every 5 min | Bull repeatable | Integrations | Active |
| Telematics Sync | Every 2 min | Bull repeatable | Integrations | Active |
| Alert Escalation | Every 1 min | @Cron | Alerts | Active |
| Daily Alert Digest | Daily @ 6 AM | @Cron | Alerts | Active |
| Shift Handoff Summary | 6 AM & 6 PM | @Cron | Alerts | Active |
| Snooze Expiry | Every 1 min | @Cron | Alerts | Active |
Integration Sync Jobs (BullMQ)
All sync jobs run on the sync Bull queue with concurrency 2. They are registered as repeatable jobs by SyncQueueModule on application startup.
Architecture
SyncQueueModule (OnModuleInit)
→ Reads active IntegrationConfigs from DB
→ Registers Bull repeatable jobs per integration + type
→ Removes stale repeatables on restart
SyncProcessor (Bull Worker)
→ Receives sync jobs from the queue
→ Creates/updates Job records in the jobs table
→ Calls TmsSyncService / EldSyncService
→ Emits SSE events (sync:started, sync:completed, sync:failed)
→ Updates IntegrationConfig timestampsKey Files
| File | Purpose |
|---|---|
src/infrastructure/sync/sync-job.types.ts | Type definitions: SyncJobData, SyncJobType, SyncResult |
src/infrastructure/sync/sync.processor.ts | Bull worker that processes all 5 sync types |
src/infrastructure/sync/sync-queue.module.ts | Registers repeatable jobs on startup |
src/infrastructure/queue/queue.constants.ts | Queue name constants (SYNC, DOCUMENT_PROCESSING) |
Sync Types
Drivers Sync
Schedule: Every 15 min (configurable via SYNC_DRIVERS_CRON env var)
Integration type: TMS
What it does: Calls TmsSyncService.syncDrivers() to pull driver roster from TMS.
Vehicles Sync
Schedule: Every 15 min (configurable via SYNC_VEHICLES_CRON env var)
Integration type: TMS
What it does: Calls TmsSyncService.syncVehicles() to pull vehicle fleet from TMS, then enriches with ELD telematics metadata.
Loads Sync
Schedule: Every 15 min (configurable via SYNC_LOADS_CRON env var)
Integration type: TMS
What it does: Calls TmsSyncService.syncLoads() to pull active loads from TMS.
HOS Sync
Schedule: Every 5 min (configurable via SYNC_HOS_CRON env var)
Integration type: ELD
What it does: Calls EldSyncService.syncHos() to pull HOS clock data from Samsara.
Telematics Sync
Schedule: Every 2 min (configurable via SYNC_TELEMATICS_CRON env var)
Integration type: ELD
What it does: Calls EldSyncService.syncTelematics() to pull vehicle GPS locations from ELD adapter and upsert into vehicle_telematics table.
Job Tracking
All sync jobs are tracked in the jobs table with category: 'sync'. The job lifecycle:
queued → processing → completed / failedEach job record stores:
inputData: integration ID, name, type, trigger sourceresultData: records processed/created/updatederrorDetails: error message and stack on failure
Manual Sync
Manual syncs are triggered via the integrations controller:
POST /integrations/fleet/sync— enqueue drivers + vehicles + loadsPOST /integrations/eld/sync-hos— enqueue HOS syncPOST /integrations/eld/sync-telematics— enqueue telematics sync
These create Job records upfront and return { success, message, jobIds }.
Bull Board
Sync queue status is visible at /api/v1/admin/queues via Bull Board. Shows active, waiting, completed, and failed jobs.
Alerts Domain (@Cron)
These jobs handle alert lifecycle automation using @nestjs/schedule.
Alert Escalation
File: src/domains/operations/alerts/services/escalation.service.ts
Schedule: @Cron(CronExpression.EVERY_MINUTE)
Monitors active alerts for SLA violations. Unacknowledged alerts beyond their acknowledgeSlaMinutes get escalated and SSE events emitted.
Daily Alert Digest
File: src/domains/operations/alerts/services/alert-digest.service.ts
Schedule: @Cron('0 6 * * *') (daily at 6:00 AM)
Sends HTML email summary of the last 24 hours of alert activity to dispatchers, admins, and owners.
Shift Handoff Summary
File: src/domains/operations/alerts/services/alert-digest.service.ts
Schedule: @Cron('0 6,18 * * *') (6:00 AM and 6:00 PM)
Lists top 20 unresolved alerts for incoming shift teams.
Snooze Expiry
File: src/domains/operations/alerts/services/auto-resolution.service.ts
Schedule: @Cron(CronExpression.EVERY_MINUTE)
Reactivates snoozed alerts whose snooze period has expired.
Adding a New Sync Job
- Add the job type to
SyncJobTypeinsync-job.types.ts - Add the sync logic as a private method in
SyncProcessor - Add the repeatable registration in
SyncQueueModule.registerRepeatableJobs() - Add manual trigger support in
IntegrationsControllerif needed - Update this page
Adding a New @Cron Job
- Create or update a service with a
@Cron()decorated method - Register the service in its domain module’s
providersarray - Use per-iteration error handling for batch processing
- Update this page