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

lifecycle.mddocs/guides/

Job Lifecycle

Guide to understanding and managing job lifecycle in gocron.

Overview

Every job in gocron goes through distinct lifecycle phases from creation to removal.

Lifecycle Phases

1. Creation

Jobs are created using NewJob:

j, err := s.NewJob(
    gocron.DurationJob(time.Minute),
    gocron.NewTask(myFunc),
)

At creation, gocron:

  • Validates job definition
  • Calculates initial next run time
  • Registers job with scheduler
  • Returns job handle

2. Scheduling

After Start(), scheduler begins tracking next run times:

s.Start()

Scheduler maintains internal queue of upcoming runs.

3. Execution

At scheduled time, job executes:

  1. Checks concurrency limits (singleton, global)
  2. Acquires distributed lock (if configured)
  3. Runs task function
  4. Releases lock
  5. Calculates next run time

4. Completion

After execution:

  • Events fire (AfterJobRuns, AfterJobRunsWithError)
  • Next run time calculated
  • Job rescheduled (if not one-time)

5. Removal

Jobs can be explicitly removed:

err := s.RemoveJob(j.ID())

Or removed automatically (one-time jobs after execution).

Lifecycle Events

Monitor lifecycle with event listeners:

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    gocron.NewTask(myFunc),
    gocron.WithEventListeners(
        gocron.BeforeJobRuns(func(jobID uuid.UUID, jobName string) {
            log.Printf("Starting: %s", jobName)
        }),
        gocron.AfterJobRuns(func(jobID uuid.UUID, jobName string) {
            log.Printf("Completed: %s", jobName)
        }),
        gocron.AfterJobRunsWithError(func(jobID uuid.UUID, jobName string, err error) {
            log.Printf("Failed: %s - %v", jobName, err)
        }),
    ),
)

State Transitions

[Created] --Start()--> [Scheduled] --Trigger--> [Running] --Complete--> [Scheduled]
                                                    |
                                                    +--Remove()--> [Removed]

One-time jobs:

[Created] --Start()--> [Scheduled] --Trigger--> [Running] --Complete--> [Removed]

Managing Job State

Query Job Information

// Get next run time
nextRun, err := j.NextRun()

// Get last run time
lastRun, err := j.LastRun()

// Get job name
name := j.Name()

// Get job ID
id := j.ID()

Update Jobs

// Update job (replace definition)
newJob, err := s.Update(j.ID(),
    gocron.DurationJob(2*time.Minute),
    gocron.NewTask(newFunc),
)

Run Jobs Manually

// Trigger immediate execution
err := j.RunNow()

Scheduler Lifecycle

Starting

s, _ := gocron.NewScheduler()

// Add jobs...

s.Start() // Begin scheduling

Stopping

// Graceful shutdown
err := s.Shutdown()

Shutdown process:

  1. Stop accepting new runs
  2. Wait for running jobs (up to WithStopTimeout)
  3. Cancel remaining jobs
  4. Clean up resources

Graceful Shutdown

s, _ := gocron.NewScheduler(
    gocron.WithStopTimeout(30*time.Second),
)
defer s.Shutdown()

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()

go func() {
    <-ctx.Done()
    if err := s.Shutdown(); err != nil {
        log.Printf("Shutdown error: %v", err)
    }
}()

s.Start()
select {} // block

One-Time Jobs

Special lifecycle for one-time jobs:

j, _ := s.NewJob(
    gocron.OneTimeJob(
        gocron.OneTimeJobStartDateTime(time.Now().Add(time.Hour)),
    ),
    gocron.NewTask(initializeSystem),
)

After execution, automatically removed from scheduler.

Long-Running Jobs

Handle context cancellation:

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    gocron.NewTask(func(ctx context.Context) {
        for {
            select {
            case <-ctx.Done():
                log.Println("Job cancelled, cleaning up...")
                return
            default:
                doWork()
                time.Sleep(time.Second)
            }
        }
    }),
)

Monitoring Lifecycle

Use SchedulerMonitor for detailed lifecycle tracking:

type myMonitor struct{}

func (m *myMonitor) JobScheduled(jobID uuid.UUID, job gocron.Job) {
    log.Printf("Scheduled: %s", job.Name())
}

func (m *myMonitor) JobStarted(jobID uuid.UUID, job gocron.Job) {
    log.Printf("Started: %s", job.Name())
}

func (m *myMonitor) JobCompleted(jobID uuid.UUID, job gocron.Job, err error) {
    if err != nil {
        log.Printf("Failed: %s - %v", job.Name(), err)
    } else {
        log.Printf("Completed: %s", job.Name())
    }
}

See Also

  • Scheduler Lifecycle API - API reference
  • Event Options - Event listeners
  • Timing Options - Start/stop times
  • Observability Guide - Lifecycle monitoring
  • Advanced: Graceful Shutdown - Shutdown patterns

Install with Tessl CLI

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

docs

index.md

tile.json