Schedules
Status: [Designed] -- Area 2 of the Grove language. Syntax and semantics are defined; implementation is in progress.
Schedules start workflows on a recurring basis using cron expressions. Where triggers react to domain events, schedules react to time -- running batch jobs, sending periodic reports, performing maintenance tasks, or polling external systems at fixed intervals.
Prerequisites: Workflows Overview, Concepts Overview. What you'll learn: How to declare cron-based schedules, map static inputs to workflows, and understand schedule execution semantics.
Declaring a Schedule
A schedule binds a cron expression to a workflow:
schedule daily_reconciliation {
when "0 2 * * *"
start reconcile_payments v1 {
run_type: "daily"
}
}
The declaration has three parts:
- Name --
daily_reconciliation. A unique name within the module. - When clause --
when "0 2 * * *". A cron expression that defines the schedule. - Start block --
start reconcile_payments v1 { ... }. The workflow to start and its input mappings.
Cron Expressions
The when clause accepts standard five-field cron expressions:
minute hour day-of-month month day-of-week
0 2 * * *
| Field | Values | Special Characters |
|---|---|---|
| Minute | 0-59 | , - * / |
| Hour | 0-23 | , - * / |
| Day of month | 1-31 | , - * / |
| Month | 1-12 | , - * / |
| Day of week | 0-6 (0 = Sunday) | , - * / |
Examples
| Expression | Description |
|---|---|
"0 * * * *" | Every hour, on the hour |
"*/15 * * * *" | Every 15 minutes |
"0 2 * * *" | Daily at 2:00 AM |
"0 9 * * 1" | Every Monday at 9:00 AM |
"0 0 1 * *" | First day of every month at midnight |
"0 8,17 * * 1-5" | Weekdays at 8:00 AM and 5:00 PM |
Field Mappings
The start block provides static input values to the workflow. Since schedules are time-based (not event-based), there is no event root object. Mappings use literal values:
schedule weekly_report {
when "0 9 * * 1"
start generate_report v1 {
report_type: "weekly_summary"
lookback_days: 7
format: "pdf"
}
}
The workflow receives these values as its input every time the schedule fires.
Execution Semantics
- At-most-once per tick -- The runtime ensures that each scheduled time slot produces at most one workflow execution, even if the scheduler process restarts.
- No catch-up by default -- If the system is down when a schedule should fire, the missed execution is not retroactively started. This behavior may be configurable at the runtime level.
- UTC -- Cron expressions are evaluated in UTC. Time zone handling is a runtime configuration concern.
- Idempotent -- As with triggers, design the target workflow to be idempotent. In edge cases (scheduler failover), a schedule may fire twice for the same time slot.
Multiple Schedules
A module can declare any number of schedules:
schedule hourly_sync {
when "0 * * * *"
start sync_inventory v1 {
source: "warehouse"
}
}
schedule daily_cleanup {
when "0 3 * * *"
start cleanup_expired_sessions v1 {
max_age_days: 30
}
}
schedule monthly_billing {
when "0 0 1 * *"
start generate_invoices v1 {
billing_period: "monthly"
}
}
Schedules vs. Triggers
| Concern | Trigger | Schedule |
|---|---|---|
| Activation | Domain event emitted | Cron tick |
| Input source | Event fields and metadata | Static literal values |
| Frequency | Per event | Fixed time intervals |
| Use case | Reactive orchestration | Batch jobs, periodic tasks |
Both triggers and schedules start the same kind of workflows. The difference is only in what initiates the execution and what data is available as input.
See Also
- Triggers -- Event-driven workflow starts
- Workflows Overview -- The workflows that schedules start
- Durable Execution --
sleepas an alternative for in-workflow delays - Full Language Reference -- Schedule grammar (section 6.15)