Schedule Config
ScheduleConfig
Section titled “ScheduleConfig”Top-level scheduler configuration added to AppConfig.
use horsies::{AppConfig, ScheduleConfig, TaskSchedule};
let config = AppConfig { schedule: Some(ScheduleConfig::new(vec![/* ... */]).check_interval_seconds(1)), ..AppConfig::for_database_url("postgresql://...")};Fields
Section titled “Fields”| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | true | Enable/disable scheduler |
check_interval_seconds | u32 | 1 | Seconds between schedule checks (1-60) |
schedules | Vec<TaskSchedule> | [] | Schedule definitions |
TaskSchedule
Section titled “TaskSchedule”Defines a single scheduled task.
use horsies::{TaskSchedule, SchedulePattern, DailySchedule};use chrono::NaiveTime;
TaskSchedule::new( "daily-report", "generate_report", SchedulePattern::Daily(DailySchedule { time: NaiveTime::from_hms_opt(9, 0, 0).unwrap(), }),).kwargs(serde_json::json!({"format": "pdf"}))Fields
Section titled “Fields”| Field | Type | Default | Description |
|---|---|---|---|
name | String | required | Unique schedule identifier |
task_name | String | required | Registered task name |
pattern | SchedulePattern | required | When to run |
args | serde_json::Value | Null | Positional arguments (JSON array) |
kwargs | serde_json::Value | Null | Keyword arguments (JSON object) |
queue_name | Option<String> | None | Queue override (CUSTOM mode) |
enabled | bool | true | Enable/disable this schedule |
timezone | String | "UTC" | Timezone for schedule evaluation |
catch_up_missed | bool | false | Execute missed runs on restart |
max_catch_up_runs | u32 | 100 | Maximum runs to enqueue per scheduler tick when catch_up_missed=true (range: 1-10000) |
Must be unique across all schedules. Used for state tracking:
TaskSchedule::new( "hourly-sync", "sync_data", SchedulePattern::Interval(IntervalSchedule { hours: Some(1), ..Default::default() }),);TaskSchedule::new( "daily-cleanup", "cleanup_old_data", SchedulePattern::Daily(DailySchedule { time: NaiveTime::from_hms_opt(3, 0, 0).unwrap(), }),);Task Name
Section titled “Task Name”Must match a registered #[task]:
#[task("send_notification")]async fn send_notification(input: NotifyInput) -> Result<(), TaskError> { // ... Ok(())}
TaskSchedule::new( "notify-users", "send_notification", // Must match SchedulePattern::Daily(DailySchedule { time: NaiveTime::from_hms_opt(9, 0, 0).unwrap(), }),)Arguments
Section titled “Arguments”Pass arguments to the scheduled task as serialized JSON:
#[task("process_region")]async fn process_region(input: RegionInput) -> Result<(), TaskError> { // ... Ok(())}
TaskSchedule::new( "sync-us-east", "process_region", SchedulePattern::Interval(IntervalSchedule { hours: Some(1), ..Default::default() }),).kwargs(serde_json::json!({ "region": "us-east", "full_sync": true,}))Queue Override
Section titled “Queue Override”In CUSTOM mode, override the task’s default queue:
#[task("background_job", queue = "normal")]async fn background_job() -> Result<(), TaskError> { // ... Ok(())}
TaskSchedule::new( "priority-job", "background_job", SchedulePattern::Daily(DailySchedule { time: NaiveTime::from_hms_opt(9, 0, 0).unwrap(), }),) .queue("critical") // Override to higher priority queueTimezone
Section titled “Timezone”Schedule evaluated in specified timezone:
// Runs at 9 AM New York time (EST/EDT)TaskSchedule::new( "morning-task", "my_task", SchedulePattern::Daily(DailySchedule { time: NaiveTime::from_hms_opt(9, 0, 0).unwrap(), }),).timezone("America/New_York")Uses IANA timezone names via chrono-tz. Common values:
"UTC"- Coordinated Universal Time"America/New_York"- US Eastern"America/Los_Angeles"- US Pacific"Europe/London"- UK"Asia/Tokyo"- Japan
Catch-Up
Section titled “Catch-Up”When catch_up_missed=true, missed runs are executed:
TaskSchedule::new( "hourly-sync", "sync_data", SchedulePattern::Interval(IntervalSchedule { hours: Some(1), ..Default::default() }),).catch_up_missed(true)If scheduler was down 3 hours, 3 tasks are enqueued on restart.
Use for:
- Data synchronization (must process all intervals)
- Compliance reporting (all periods must be covered)
Avoid for:
- Notifications (users do not want 24 emails at once)
- Status updates (only latest matters)
Disabling Schedules
Section titled “Disabling Schedules”Disable individual schedules:
TaskSchedule::new( "deprecated-job", "old_task", SchedulePattern::Daily(DailySchedule { time: NaiveTime::from_hms_opt(9, 0, 0).unwrap(), }),) .enabled(false) // Will not runDisable entire scheduler:
ScheduleConfig::new(vec![]) .enabled(false) // No schedules run .check_interval_seconds(30)Configuration Changes
Section titled “Configuration Changes”When schedule configuration changes:
- Scheduler detects via
config_hash - Recalculates
next_run_atfrom current time - Logs warning about configuration change
This prevents issues when:
- Pattern changes (e.g., hourly to daily)
- Timezone changes
- Time changes within pattern
Validation
Section titled “Validation”At scheduler startup, validates:
- Task exists:
task_namemust be registered - Queue valid:
queue_namemust match configured queue (CUSTOM mode) - Timezone valid: Must be a recognized IANA timezone name
- Schedule names unique: No duplicate
namevalues
// Will fail at startup:TaskSchedule::new( "bad", "nonexistent_task", // Not registered SchedulePattern::Daily(DailySchedule { time: NaiveTime::from_hms_opt(9, 0, 0).unwrap(), }),)