Schedule
Cron scheduling and task dispatch
schedule maps cron expressions to task tasks. It handles scheduling time, while real execution is delegated to task; it can also dispatch asynchronously through queue.
Install
go get github.com/duxweb/runa/schedule github.com/duxweb/runa/task
Connect to an application
package main
import (
"context"
"fmt"
"github.com/duxweb/runa"
"github.com/duxweb/runa/provider"
"github.com/duxweb/runa/schedule"
"github.com/duxweb/runa/task"
)
type ReportPayload struct {
Name string `json:"name"`
}
type appModule struct {
provider.ModuleBase
}
func (appModule) Name() string { return "app" }
func (appModule) Register(ctx context.Context, app provider.Context) error {
tasks, err := provider.Invoke[*task.Registry](app)
if err != nil {
return err
}
schedules, err := provider.Invoke[*schedule.Registry](app)
if err != nil {
return err
}
tasks.Register("report.daily", func(ctx context.Context, item *task.TaskOf[ReportPayload]) error {
fmt.Println("run", item.Payload.Name)
return nil
})
schedules.Register("daily-report", "0 2 * * *", "report.daily", ReportPayload{Name: "daily"})
return nil
}
func main() {
app := runa.New()
app.Install(task.Provider(), schedule.Provider())
app.Module(appModule{})
if err := app.Run(context.Background()); err != nil {
panic(err)
}
}
Run the scheduler process with:
go run . schedule:run
Standalone New usage
tasks := task.New()
schedules := schedule.New()
schedules.Register("daily-report", "0 2 * * *", "report.daily", ReportPayload{Name: "daily"})
unit := schedule.NewUnit(schedules, tasks)
_ = unit
Standalone usage requires starting schedule.NewUnit(...) yourself, or using the registry only for schedule metadata.
Config
schedule currently has no TOML config. Schedule entries are registered in code so task names, payload types, and cron expressions stay together.
When a schedule does not set its own timezone, cron expressions use the application timezone. Set it with runa.Timezone("Asia/Shanghai") or timezone in config/app.toml.
Schedule options
schedules.Register("daily-report", "0 2 * * *", "report.daily", payload,
schedule.Timezone("Asia/Shanghai"),
schedule.Queue("default"),
schedule.SkipIfRunning(),
schedule.Meta("owner", "ops"),
)
Timezone(name)sets the timezone for this schedule and overrides the application timezone.Queue(name)executes through a queue.Direct()forces direct execution.SkipIfRunning()skips this run if the previous run is still active.DelayIfRunning()delays this run if the previous run is still active.Enabled(false)pauses the schedule.
Commands
After installing schedule.Provider(), these commands are registered:
go run . schedule:list
go run . schedule:run
schedule:run starts the whole scheduler worker and does not accept a single schedule name argument.
Common mistakes
Expecting schedule to contain business logic
schedule triggers work on time. Put business work in task handlers, services, or Modules, then call them from scheduled jobs.
Passing a job name to schedule:run
schedule:run starts the scheduler service. It does not accept one schedule name as an argument.
Cron timezone is unclear
If you do not call schedule.Timezone(...), the schedule follows the application timezone. For cross-timezone deployments, set runa.Timezone("Asia/Shanghai") at the application entrypoint first; use schedule.Timezone(...) only when one schedule needs a different timezone.
API quick reference
schedule.New()creates a standalone registry.schedule.Provider()connects to the framework lifecycle.schedule.Default()reads*schedule.Registryfrom default DI.registry.Register(name, spec, taskName, payload, options...)registers a schedule.schedule.NewUnit(registry, tasks)creates a scheduler Host unit.