Scheduled Jobs

The SALLY backend uses two scheduling mechanisms:

  1. BullMQ repeatable jobs (@nestjs/bullmq) — for integration sync jobs that need concurrency control, retry, and visibility via Bull Board
  2. 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

JobFrequencyMechanismDomainStatus
Drivers SyncEvery 15 minBull repeatableIntegrationsActive
Vehicles SyncEvery 15 minBull repeatableIntegrationsActive
Loads SyncEvery 15 minBull repeatableIntegrationsActive
HOS SyncEvery 5 minBull repeatableIntegrationsActive
Telematics SyncEvery 2 minBull repeatableIntegrationsActive
Alert EscalationEvery 1 min@CronAlertsActive
Daily Alert DigestDaily @ 6 AM@CronAlertsActive
Shift Handoff Summary6 AM & 6 PM@CronAlertsActive
Snooze ExpiryEvery 1 min@CronAlertsActive

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 timestamps

Key Files

FilePurpose
src/infrastructure/sync/sync-job.types.tsType definitions: SyncJobData, SyncJobType, SyncResult
src/infrastructure/sync/sync.processor.tsBull worker that processes all 5 sync types
src/infrastructure/sync/sync-queue.module.tsRegisters repeatable jobs on startup
src/infrastructure/queue/queue.constants.tsQueue 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 / failed

Each job record stores:

  • inputData: integration ID, name, type, trigger source
  • resultData: records processed/created/updated
  • errorDetails: error message and stack on failure

Manual Sync

Manual syncs are triggered via the integrations controller:

  • POST /integrations/fleet/sync — enqueue drivers + vehicles + loads
  • POST /integrations/eld/sync-hos — enqueue HOS sync
  • POST /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

  1. Add the job type to SyncJobType in sync-job.types.ts
  2. Add the sync logic as a private method in SyncProcessor
  3. Add the repeatable registration in SyncQueueModule.registerRepeatableJobs()
  4. Add manual trigger support in IntegrationsController if needed
  5. Update this page

Adding a New @Cron Job

  1. Create or update a service with a @Cron() decorated method
  2. Register the service in its domain module’s providers array
  3. Use per-iteration error handling for batch processing
  4. Update this page