Dispatch

Cron

Distributed cron scheduling with leader election and per-tenant support.

The cron package provides distributed cron scheduling. Only the cluster leader fires cron entries, guaranteeing at-most-once job enqueue even when multiple Dispatch instances are running.

Registering a cron

Use engine.RegisterCron at startup:

type ReportInput struct {
    Format string `json:"format"`
}

var GenerateReport = job.NewDefinition("generate_report",
    func(ctx context.Context, input ReportInput) error {
        return generateDailyReport(input.Format)
    },
)

// Run every day at 9 AM
engine.RegisterCron(ctx, eng, "daily-report", "0 9 * * *",
    GenerateReport, ReportInput{Format: "pdf"})

The cron entry is persisted in the store. If an entry with the same name already exists, ErrDuplicateCron is returned (unless UpsertOnConflict is used).

Schedule format

Dispatch uses standard 5-field cron expressions (via robfig/cron/v3):

┌───────────── minute (0–59)
│ ┌─────────── hour (0–23)
│ │ ┌───────── day of month (1–31)
│ │ │ ┌─────── month (1–12)
│ │ │ │ ┌───── day of week (0–6, Sunday=0)
│ │ │ │ │
* * * * *

Common expressions:

ExpressionMeaning
* * * * *Every minute
0 * * * *Every hour
0 9 * * 1-5Weekdays at 9 AM
0 0 1 * *First of each month at midnight
@hourlyEvery hour (alias)
@dailyEvery day at midnight (alias)

Enable / Disable

Cron entries can be toggled at runtime:

POST /v1/crons/:cronId/enable
POST /v1/crons/:cronId/disable

Disabled entries are skipped by the scheduler without being deleted.

Admin API

MethodPathDescription
GET/v1/cronsList all cron entries
GET/v1/crons/:cronIdGet cron entry
POST/v1/crons/:cronId/enableEnable
POST/v1/crons/:cronId/disableDisable
DELETE/v1/crons/:cronIdDelete

Extension hook

When a cron fires and a job is enqueued, the ext.CronFired hook is called:

type CronFired interface {
    OnCronFired(ctx context.Context, entryName string, jobID id.JobID) error
}

On this page