CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/golang-github-com-go-co-op-gocron-v2

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.

Overview
Eval results
Files

tasks.mddocs/api/

Tasks

Tasks define what to run. Wrap any Go function with NewTask.

Task Type

type Task func() task

Opaque type returned by NewTask. Use with Scheduler.NewJob or Scheduler.Update.

NewTask

func NewTask(function any, parameters ...any) Task

Wraps a function and its parameters into a Task.

Parameters:

  • function: Any Go function
  • parameters: Arguments to pass to the function (must match function signature)

Errors (returned by NewJob, not NewTask):

  • ErrNewJobTaskNil: task is nil
  • ErrNewJobTaskNotFunc: function is not of kind reflect.Func
  • ErrNewJobWrongNumberOfParameters: parameter count doesn't match function signature
  • ErrNewJobWrongTypeOfParameters: parameter types don't match function signature

Basic Usage

No Parameters

task := gocron.NewTask(func() {
    fmt.Println("Simple task")
})

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    task,
)

With Parameters

task := gocron.NewTask(func(msg string, count int) {
    fmt.Printf("%s: %d\n", msg, count)
}, "Hello", 42)

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    task,
)

Multiple Parameters

task := gocron.NewTask(
    func(name string, age int, active bool) {
        fmt.Printf("%s (%d years old) is active: %v\n", name, age, active)
    },
    "Alice",
    30,
    true,
)

Named Functions

func doWork(msg string) {
    fmt.Println(msg)
}

task := gocron.NewTask(doWork, "Working...")

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    task,
)

Context Handling

Auto-Injection

If your function's first parameter is context.Context, gocron automatically injects it. Don't pass context via parameters.

// CORRECT: context auto-injected
task := gocron.NewTask(func(ctx context.Context) {
    select {
    case <-ctx.Done():
        fmt.Println("Context cancelled")
        return
    default:
        doWork()
    }
})

// INCORRECT: don't pass context manually
// task := gocron.NewTask(func(ctx context.Context) { ... }, myContext)

Context with Parameters

Context must be first parameter:

task := gocron.NewTask(func(ctx context.Context, msg string, count int) {
    for i := 0; i < count; i++ {
        select {
        case <-ctx.Done():
            return
        default:
            fmt.Println(msg, i)
            time.Sleep(time.Second)
        }
    }
}, "Processing", 10)

Custom Context

Set parent context via WithContext:

ctx, cancel := context.WithCancel(context.Background())

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    gocron.NewTask(func(ctx context.Context) {
        select {
        case <-ctx.Done():
            return
        default:
            doWork()
        }
    }),
    gocron.WithContext(ctx),
)

// Later: cancel() cancels the context
cancel()

See WithContext for details.

Error Handling

Returning Errors

Task functions can return error as the last return value. Error is captured and triggers AfterJobRunsWithError listener.

task := gocron.NewTask(func() error {
    if err := doWork(); err != nil {
        return fmt.Errorf("work failed: %w", err)
    }
    return nil
})

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    task,
    gocron.WithEventListeners(
        gocron.AfterJobRunsWithError(func(jobID uuid.UUID, jobName string, err error) {
            log.Printf("Job failed: %v", err)
        }),
    ),
)

Context with Error

task := gocron.NewTask(func(ctx context.Context) error {
    select {
    case <-ctx.Done():
        return ctx.Err()
    default:
        return doWork()
    }
})

Multiple Return Values

Task functions can return multiple values. Last return value can be error.

// Return value is ignored, error is captured
task := gocron.NewTask(func() (string, error) {
    result, err := doWorkWithResult()
    return result, err
})

Note: gocron doesn't use non-error return values. Only error is captured.

Panics

gocron automatically recovers from panics. Panic data is passed to AfterJobRunsWithPanic listener.

task := gocron.NewTask(func() {
    panic("something went wrong")
})

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    task,
    gocron.WithEventListeners(
        gocron.AfterJobRunsWithPanic(func(jobID uuid.UUID, jobName string, recoverData any) {
            log.Printf("Job panicked: %v", recoverData)
        }),
    ),
)

Note: Job is considered failed and rescheduled normally after panic.

Advanced Patterns

Closures

Task functions can be closures that capture variables:

counter := 0
task := gocron.NewTask(func() {
    counter++
    fmt.Printf("Run count: %d\n", counter)
})

Warning: Be careful with mutable shared state. Use proper synchronization if accessing from multiple jobs.

Struct Methods

type Worker struct {
    name string
}

func (w *Worker) DoWork() {
    fmt.Printf("%s working\n", w.name)
}

worker := &Worker{name: "Alice"}
task := gocron.NewTask(worker.DoWork)

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    task,
)

Dependency Injection

Pass dependencies as parameters or via closures:

// Via parameters
task := gocron.NewTask(func(db *sql.DB, logger *log.Logger) {
    logger.Println("Running job")
    doWorkWithDB(db)
}, database, logger)

// Via closure
task := gocron.NewTask(func() {
    logger.Println("Running job")
    doWorkWithDB(database)
})

Factory Functions

func createBackupTask(db *sql.DB, bucket string) gocron.Task {
    return gocron.NewTask(func(ctx context.Context) error {
        return backupDatabaseToBucket(ctx, db, bucket)
    })
}

task := createBackupTask(database, "my-bucket")

Variadic Functions

func process(items ...string) {
    for _, item := range items {
        fmt.Println(item)
    }
}

task := gocron.NewTask(process, "a", "b", "c")

Generic Functions

func processItems[T any](items []T, processor func(T)) {
    for _, item := range items {
        processor(item)
    }
}

task := gocron.NewTask(processItems[int], []int{1, 2, 3}, func(i int) {
    fmt.Println(i * 2)
})

Validation

Task validation happens at NewJob time, not NewTask time. This allows reusing tasks across jobs:

task := gocron.NewTask(func(msg string) {
    fmt.Println(msg)
}, "hello")

// Reuse task
j1, _ := s.NewJob(gocron.DurationJob(time.Minute), task)
j2, _ := s.NewJob(gocron.CronJob("0 * * * *", false), task)

Validation Errors

// Missing parameter
task := gocron.NewTask(func(msg string) {
    fmt.Println(msg)
})
j, err := s.NewJob(gocron.DurationJob(time.Minute), task)
// err: ErrNewJobWrongNumberOfParameters

// Wrong parameter type
task := gocron.NewTask(func(count int) {
    fmt.Println(count)
}, "not an int")
j, err := s.NewJob(gocron.DurationJob(time.Minute), task)
// err: ErrNewJobWrongTypeOfParameters

Common Patterns

Scheduled Cleanup

task := gocron.NewTask(func(ctx context.Context, maxAge time.Duration) error {
    return cleanupOldRecords(ctx, maxAge)
}, 24*time.Hour)

j, _ := s.NewJob(
    gocron.DailyJob(1, gocron.NewAtTimes(gocron.NewAtTime(2, 0, 0))),
    task,
)

Periodic Health Check

task := gocron.NewTask(func(ctx context.Context) error {
    return healthCheck(ctx)
})

j, _ := s.NewJob(
    gocron.DurationJob(30*time.Second),
    task,
    gocron.WithSingletonMode(gocron.LimitModeReschedule),
)

Report Generation

task := gocron.NewTask(func(ctx context.Context, reportType string, recipients []string) error {
    report, err := generateReport(ctx, reportType)
    if err != nil {
        return err
    }
    return emailReport(report, recipients)
}, "daily-sales", []string{"team@example.com"})

j, _ := s.NewJob(
    gocron.DailyJob(1, gocron.NewAtTimes(gocron.NewAtTime(9, 0, 0))),
    task,
)

See Also

Install with Tessl CLI

npx tessl i tessl/golang-github-com-go-co-op-gocron-v2

docs

api

errors.md

quick-reference.md

tasks.md

index.md

tile.json