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
- ScheduleCache loads all active schedules from PostgreSQL every 60 seconds (configurable)
- ScheduleTimer checks for due schedules every 1 second (configurable)
- When
nextRunAt <= now, the scheduler fires the schedule by POSTing a trigger to the Receiver - After firing:
- Cron schedules:
nextRunAtis recomputed usingcron-parserwith timezone support - Datetime schedules:
nextRunAtis set tonull(one-time, completed)
- Cron schedules:
lastRunAtis updated with the current timestamp
Schedule Types
Cron Schedules
Recurring schedules using standard cron expressions with timezone support.
| Field | Description | Example |
|---|---|---|
scheduleType | Must be "cron" | "cron" |
cronExpression | Standard 5-field cron | "*/5 * * * *" (every 5 min) |
timezone | IANA timezone | "America/New_York" |
Common cron patterns:
| Expression | Meaning |
|---|---|
*/5 * * * * | Every 5 minutes |
0 * * * * | Every hour on the hour |
0 9 * * * | Daily at 9:00 AM |
0 9 * * 1-5 | Weekdays 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.
| Field | Description | Example |
|---|---|---|
scheduleType | Must be "datetime" | "datetime" |
scheduledAt | ISO 8601 datetime | "2026-04-15T14:30:00Z" |
timezone | IANA 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:
| State | nextRunAt | Description |
|---|---|---|
| Active | Future timestamp | Will fire at the specified time |
| Completed | null | One-time schedule that has fired, or manually deactivated |
To reactivate a completed datetime schedule, update it with a new future scheduledAt value.
Configuration
| Environment Variable | Default | Description |
|---|---|---|
DATABASE_URL | (required) | PostgreSQL connection string |
RECEIVER_URL | https://receiver.rocketwavelabs.io | Receiver endpoint URL |
SCHEDULE_REFRESH_INTERVAL_MS | 60000 | How often to reload schedules from DB (ms) |
SCHEDULE_CHECK_INTERVAL_MS | 1000 | How often to check for due schedules (ms) |
LOG_LEVEL | info | Pino log level |
AWS_REGION | us-east-2 | AWS 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
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.
Related Topics
- Scheduled Events API — Create and manage schedules via API
- Consumer Evaluator — How triggered workflows are processed
- Workflow Canvas — Build workflows to schedule