A Golang job scheduling library that lets you run Go functions at pre-determined intervals using cron expressions, fixed durations, daily, weekly, monthly, or one-time schedules with support for distributed deployments.
Options for distributed job coordination.
func WithDistributedJobLocker(locker Locker) JobOptionSets per-job distributed locker, overriding global locker.
Errors:
ErrWithDistributedJobLockerNil: locker is nilSee Locker interface.
// Scheduler has global locker
s, _ := gocron.NewScheduler(
gocron.WithDistributedLocker(globalLocker),
)
// This job uses different locker
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(myFunc),
gocron.WithName("special-job"),
gocron.WithDistributedJobLocker(specialLocker),
)Uses: Different lock backends per job, different TTLs, testing
func WithDisabledDistributedJobLocker(disabled bool) JobOptionDisables global distributed locker for specific job.
// Scheduler has global locker
s, _ := gocron.NewScheduler(
gocron.WithDistributedLocker(globalLocker),
)
// This job skips locking
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(localOnlyFunc),
gocron.WithDisabledDistributedJobLocker(true),
)Uses: Local-only jobs (node-specific health checks), jobs that don't need coordination, testing
Set at scheduler level:
func WithDistributedElector(elector Elector) SchedulerOptionEnables leader election. Only the leader instance runs jobs.
See Elector interface and Creating Schedulers.
func WithDistributedLocker(locker Locker) SchedulerOptionEnables per-job distributed locking for all jobs.
See Locker interface and Creating Schedulers.
// Global Redis locker
redisLocker := &RedisLocker{client: redisClient}
s, _ := gocron.NewScheduler(
gocron.WithDistributedLocker(redisLocker),
)
// Most jobs use Redis
j1, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(normalTask),
)
// Critical job uses etcd locker
etcdLocker := &EtcdLocker{client: etcdClient}
j2, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(criticalTask),
gocron.WithName("critical-job"),
gocron.WithDistributedJobLocker(etcdLocker),
)s, _ := gocron.NewScheduler(
gocron.WithDistributedLocker(globalLocker),
)
// This job needs coordination
j1, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(func() {
processSharedQueue()
}),
gocron.WithName("queue-processor"),
)
// This job is node-specific, no locking needed
j2, _ := s.NewJob(
gocron.DurationJob(30*time.Second),
gocron.NewTask(func() {
checkLocalDiskSpace()
}),
gocron.WithName("disk-check"),
gocron.WithDisabledDistributedJobLocker(true),
)s, _ := gocron.NewScheduler(
gocron.WithDistributedElector(elector), // Only leader runs jobs
gocron.WithDistributedLocker(locker), // Leader uses locks for safety
)
// Coordinated job (runs on leader only, with lock)
j1, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(coordinatedTask),
gocron.WithName("coordinated"),
)
// Node-local job (runs on all nodes, no election/locking)
j2, _ := s.NewJob(
gocron.DurationJob(30*time.Second),
gocron.NewTask(localMetrics),
gocron.WithName("local-metrics"),
gocron.WithDisabledDistributedJobLocker(true),
)j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(myFunc),
gocron.WithName("locked-job"),
gocron.WithEventListeners(
gocron.AfterLockError(func(jobID uuid.UUID, jobName string, err error) {
log.Printf("Lock acquisition failed for %s: %v", jobName, err)
metricsLockContentions.WithLabelValues(jobName).Inc()
}),
),
)When set via WithDistributedLocker:
WithName)AfterLockError listener is called on failureWhen set via WithDistributedJobLocker:
When set via WithDisabledDistributedJobLocker:
If both elector and locker are set:
s, _ := gocron.NewScheduler(
gocron.WithDistributedElector(elector), // Only leader runs
gocron.WithDistributedLocker(locker), // Leader still uses locks
)Disable distribution for local testing:
var s gocron.Scheduler
if isProduction {
s, _ = gocron.NewScheduler(
gocron.WithDistributedElector(elector),
gocron.WithDistributedLocker(locker),
)
} else {
s, _ = gocron.NewScheduler()
}
// Jobs work in both modes
j, _ := s.NewJob(
gocron.DurationJob(time.Minute),
gocron.NewTask(myFunc),
gocron.WithName("my-job"),
)AfterLockError listenerAfterLockError frequencyInstall with Tessl CLI
npx tessl i tessl/golang-github-com-go-co-op-gocron-v2docs
api
examples
guides