A context-aware Go scheduler with typed workflows, explicit lifecycle control, missed-fire policies, and optional distributed coordination. Requires Go 1.27.
go get github.com/libtnb/cronc := cron.MustNew(cron.WithLocation(time.UTC))
_, err := c.Add("0 9 * * MON-FRI", cron.JobFunc(func(ctx context.Context) error {
return sendDigest(ctx)
}), cron.WithName("digest"))
if err != nil {
log.Fatal(err)
}
if err := c.Start(); err != nil {
log.Fatal(err)
}
defer func() {
if err := c.Stop(context.Background()); err != nil {
log.Printf("stop cron: %v", err)
}
}()The built-in parser uses five fields and accepts descriptors such as @hourly,
@every 10s, and CRON_TZ=Asia/Taipei. WithSecondsField enables an optional
leading seconds field. Programmatic schedules include OnceAt, ConstantDelay,
AlignedDelay, TriggeredSchedule, Union, and Filter.
Entries have stable EntryID values and may be paused, resumed, updated,
triggered, inspected, or removed. Stop cancels running jobs; Drain lets them
finish. Both are bounded by the supplied context. Missed fires default to
MissedSkip; MissedRunOnce and MissedRunAll enable catch-up.
Go 1.27 generic methods keep workflow data flow type-safe:
b := workflow.New(workflow.WithMaxParallelism(8))
download := b.Step[[]byte]("download", func(ctx context.Context, _ workflow.Inputs) ([]byte, error) {
return fetch(ctx)
})
b.Step[int]("store", func(ctx context.Context, in workflow.Inputs) (int, error) {
data, ok := in.Get(download)
if !ok {
return 0, errors.New("download output unavailable")
}
return save(ctx, data)
}, workflow.After(download, workflow.OnSuccess))
wf := b.MustBuild() // Build freezes the graph.The executor is bounded (32 concurrent steps by default). Dependencies may use
OnSuccess, OnFailure, OnSkipped, or OnComplete; Execute returns typed
outputs and per-step results.
Claimer.Claim elects one replica for each keyed fire. Elector.IsLeader
restricts automatic fires to the current leader. false, nil is normal
contention/follower state; backend errors fail closed. Manual triggers bypass
coordination.
c := cron.MustNew(cron.WithClaimer(claimer))
_, err := c.Add("0 * * * *", job, cron.WithKey("hourly-report"))WithKey is the stable cross-replica identity; WithName is only a display
label. Redis and PostgreSQL implementations are separate modules:
go get github.com/libtnb/cron/coordination/redis
go get github.com/libtnb/cron/coordination/postgresWithObservers delivers typed Event values asynchronously through a bounded,
lossy queue. WithRecorder receives the same events inline and is intended for
fast, concurrency-safe metrics recorders. Prometheus support lives in
contrib/prometheus; OpenTelemetry job tracing lives in contrib/otel.
| Package | Purpose |
|---|---|
cron |
Scheduler, parser, lifecycle, events |
cron/workflow |
Typed bounded DAG executor |
cron/wrap |
Job wrappers |
cron/parserext |
Quartz day tokens |
cron/coordination/redis |
Redis claimer and elector |
cron/coordination/postgres |
PostgreSQL claimer and elector |
cron/contrib/prometheus |
Prometheus recorder |
cron/contrib/otel |
OpenTelemetry wrapper |
API details and examples are on pkg.go.dev.