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/api/scheduler/

Scheduler Lifecycle

How to start, pause, and shut down schedulers.

Start

Start()

Starts the scheduler. Non-blocking; jobs are scheduled in the background. Can be called multiple times (idempotent).

s.Start()

StopJobs

StopJobs() error

Stops scheduling new job runs but keeps the scheduler alive. Running jobs continue. Returns ErrStopJobsTimedOut if jobs don't finish within the configured StopTimeout.

Can be resumed with Start().

err := s.StopJobs()
if errors.Is(err, gocron.ErrStopJobsTimedOut) {
    log.Println("Some jobs didn't finish in time")
}

Shutdown

Shutdown() error

Gracefully shuts down the scheduler. Waits for running jobs to complete up to the configured StopTimeout. Returns ErrStopSchedulerTimedOut if timeout is exceeded.

err := s.Shutdown()
if errors.Is(err, gocron.ErrStopSchedulerTimedOut) {
    log.Println("Shutdown timed out")
}

JobsWaitingInQueue

JobsWaitingInQueue() int

Returns the number of jobs waiting in the execution queue (only relevant when using concurrency limits).

queueSize := s.JobsWaitingInQueue()

Usage Examples

Basic Lifecycle

s, _ := gocron.NewScheduler()

// Add jobs
j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    gocron.NewTask(myFunc),
)

// Start
s.Start()

// Run for a while...
time.Sleep(5 * time.Minute)

// Graceful shutdown
err := s.Shutdown()
if err != nil {
    log.Printf("Shutdown error: %v", err)
}

Pause and Resume

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

// Add jobs
j, _ := s.NewJob(...)

// Start
s.Start()

// Pause (jobs keep running, no new schedules)
s.StopJobs()

// Resume
s.Start()

// Graceful shutdown
err := s.Shutdown()
if errors.Is(err, gocron.ErrStopSchedulerTimedOut) {
    log.Println("Timeout waiting for jobs")
}

With Stop Timeout

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

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    gocron.NewTask(func(ctx context.Context) {
        select {
        case <-ctx.Done():
            log.Println("Context cancelled, cleaning up...")
            return
        case <-time.After(time.Minute):
            log.Println("Work complete")
        }
    }),
)

s.Start()

// Later: shutdown waits up to 30 seconds for running jobs
err := s.Shutdown()
if errors.Is(err, gocron.ErrStopSchedulerTimedOut) {
    log.Println("Some jobs didn't finish within timeout")
}

Monitoring Queue

s, _ := gocron.NewScheduler(
    gocron.WithLimitConcurrentJobs(2, gocron.LimitModeWait),
)

// Add multiple jobs
for i := 0; i < 10; i++ {
    s.NewJob(
        gocron.DurationJob(time.Second),
        gocron.NewTask(func() {
            time.Sleep(5 * time.Second)
        }),
    )
}

s.Start()

// Check queue periodically
ticker := time.NewTicker(time.Second)
defer ticker.Stop()

for range ticker.C {
    queueSize := s.JobsWaitingInQueue()
    fmt.Printf("Jobs in queue: %d\n", queueSize)
}

Deferred Shutdown

func main() {
    s, err := gocron.NewScheduler(
        gocron.WithStopTimeout(30*time.Second),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer s.Shutdown()

    j, err := s.NewJob(
        gocron.DurationJob(time.Minute),
        gocron.NewTask(doWork),
    )
    if err != nil {
        log.Fatal(err)
    }

    s.Start()

    // Block until signal
    sigChan := make(chan os.Signal, 1)
    signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
    <-sigChan

    log.Println("Shutting down...")
    // deferred Shutdown() will be called
}

Lifecycle States

The scheduler has the following states:

  1. Created: After NewScheduler(), before Start()
  2. Running: After Start(), jobs are being scheduled and executed
  3. Paused: After StopJobs(), no new job runs but scheduler is alive
  4. Shutdown: After Shutdown(), scheduler is stopped and cannot be restarted

State transitions:

  • Created → Running: Start()
  • Running → Paused: StopJobs()
  • Paused → Running: Start()
  • Running → Shutdown: Shutdown()
  • Paused → Shutdown: Shutdown()

Error Handling

ErrStopJobsTimedOut

Returned by StopJobs() when jobs don't complete within the configured timeout.

err := s.StopJobs()
if errors.Is(err, gocron.ErrStopJobsTimedOut) {
    // Some jobs are still running
    log.Println("Timeout waiting for jobs to stop")
}

ErrStopSchedulerTimedOut

Returned by Shutdown() when the scheduler doesn't stop within the configured timeout.

err := s.Shutdown()
if errors.Is(err, gocron.ErrStopSchedulerTimedOut) {
    // Scheduler didn't stop cleanly
    log.Println("Timeout waiting for scheduler to shutdown")
}

Best Practices

  1. Always defer Shutdown(): Ensures cleanup even if main panics
  2. Set appropriate timeout: Use WithStopTimeout() based on longest job duration
  3. Handle context in tasks: Jobs should respect context cancellation for graceful shutdown
  4. Check timeout errors: Log or handle timeout errors appropriately

See Also

  • Creating Schedulers - NewScheduler and options
  • Managing Jobs - Add, update, remove jobs
  • Lifecycle Guide - Detailed lifecycle management
  • Concurrency Guide - Queue management

Install with Tessl CLI

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

docs

api

errors.md

quick-reference.md

tasks.md

index.md

tile.json