Skip to main content

Scheduler

The Scheduler is a long-running worker service that executes workflow triggers on a schedule. It polls the database for due schedules and fires them through the Receiver into Kinesis for processing by the Consumer.

Architecture

┌──────────────────────────────────────────────────────┐
│ Scheduler │
│ │
│ ┌────────────┐ ┌────────────┐ ┌─────────────┐ │
│ │ScheduleCache│──▶│ScheduleTimer│──▶│ HTTP POST │ │
│ │ (refresh │ │ (check │ │ Receiver │ │
│ │ every 60s)│ │ every 1s) │ │ endpoint │ │
│ └─────┬──────┘ └────────────┘ └─────────────┘ │
│ │ │
│ ┌─────▼──────┐ │
│ │ PostgreSQL │ │
│ │ ScheduledWorkflowEvent │
│ └────────────┘ │
└──────────────────────────────────────────────────────┘


┌──────────────────┐ ┌──────────────────┐
│ Receiver │───▶│ Kinesis Stream │───▶ Consumer
└──────────────────┘ └──────────────────┘

How It Works

  1. ScheduleCache loads all active schedules from PostgreSQL every 60 seconds (configurable)
  2. ScheduleTimer checks for due schedules every 1 second (configurable)
  3. When nextRunAt <= now, the scheduler fires the schedule by POSTing a trigger to the Receiver
  4. After firing:
    • Cron schedules: nextRunAt is recomputed using cron-parser with timezone support
    • Datetime schedules: nextRunAt is set to null (one-time, completed)
  5. lastRunAt is updated with the current timestamp

Schedule Types

Cron Schedules

Recurring schedules using standard cron expressions with timezone support.

FieldDescriptionExample
scheduleTypeMust be "cron""cron"
cronExpressionStandard 5-field cron"*/5 * * * *" (every 5 min)
timezoneIANA timezone"America/New_York"

Common cron patterns:

ExpressionMeaning
*/5 * * * *Every 5 minutes
0 * * * *Every hour on the hour
0 9 * * *Daily at 9:00 AM
0 9 * * 1-5Weekdays at 9:00 AM
0 0 1 * *First day of every month at midnight

Datetime Schedules

One-time schedules that fire at a specific date and time.

FieldDescriptionExample
scheduleTypeMust be "datetime""datetime"
scheduledAtISO 8601 datetime"2026-04-15T14:30:00Z"
timezoneIANA timezone"UTC"

After a datetime schedule fires, it is marked as completed (nextRunAt = null) and will not fire again.

Trigger Payload

When a schedule fires, the Scheduler POSTs the following to the Receiver:

{
"type": "workflow_trigger",
"triggerType": "scheduled",
"organizationId": "org-uuid",
"environmentId": "env-uuid",
"workflowId": "workflow-uuid",
"scheduledEventId": "schedule-uuid",
"timestamp": "2026-03-26T14:00:00.000Z"
}

The Consumer recognizes workflow_trigger messages and routes them directly to the specified workflow, bypassing the normal event condition matching.

Managing Schedules

Schedules are created and managed through the Admin Console or the Scheduled Events API.

Creating via API

POST /api/scheduled-events
Content-Type: application/json

{
"organizationId": "org-uuid",
"environmentId": "env-uuid",
"workflowId": "workflow-uuid",
"workflowEntityId": "event-entity-uuid",
"scheduleType": "cron",
"cronExpression": "0 9 * * *",
"timezone": "America/New_York"
}

Schedule Status

Schedule status is determined by the nextRunAt field:

StatenextRunAtDescription
ActiveFuture timestampWill fire at the specified time
CompletednullOne-time schedule that has fired, or manually deactivated

To reactivate a completed datetime schedule, update it with a new future scheduledAt value.

Configuration

Environment VariableDefaultDescription
DATABASE_URL(required)PostgreSQL connection string
RECEIVER_URLhttps://receiver.rocketwavelabs.ioReceiver endpoint URL
SCHEDULE_REFRESH_INTERVAL_MS60000How often to reload schedules from DB (ms)
SCHEDULE_CHECK_INTERVAL_MS1000How often to check for due schedules (ms)
LOG_LEVELinfoPino log level
AWS_REGIONus-east-2AWS region

Deployment

The Scheduler runs as a Docker container on AWS ECS Fargate:

  • Cluster: RW-Streaming-Scheduler-Cluster
  • Service: RW-Streaming-Scheduler
  • Logs: CloudWatch /ecs/rw-stream-scheduler
  • CI/CD: GitHub Actions deploys on push to main
Scaling

Running multiple scheduler replicas can cause schedules to fire more than once. Use a single replica or implement distributed locking if horizontal scaling is required.