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/examples/guides/

Examples: Lifecycle

Practical lifecycle management examples.

Event Listeners

package main

import (
    "fmt"
    "time"
    "github.com/go-co-op/gocron/v2"
    "github.com/google/uuid"
)

func main() {
    s, _ := gocron.NewScheduler()
    defer s.Shutdown()

    j, _ := s.NewJob(
        gocron.DurationJob(time.Minute),
        gocron.NewTask(func() error {
            fmt.Println("Doing work...")
            time.Sleep(time.Second)

            // Simulate occasional failure
            if time.Now().Unix()%3 == 0 {
                return fmt.Errorf("simulated error")
            }

            return nil
        }),
        gocron.WithName("monitored-job"),
        gocron.WithEventListeners(
            gocron.BeforeJobRuns(func(jobID uuid.UUID, jobName string) {
                fmt.Printf("[%v] Starting: %s\n", time.Now().Format(time.RFC3339), jobName)
            }),
            gocron.AfterJobRuns(func(jobID uuid.UUID, jobName string) {
                fmt.Printf("[%v] Completed: %s\n", time.Now().Format(time.RFC3339), jobName)
            }),
            gocron.AfterJobRunsWithError(func(jobID uuid.UUID, jobName string, err error) {
                fmt.Printf("[%v] Failed: %s - %v\n", time.Now().Format(time.RFC3339), jobName, err)
                // Send alert, log to monitoring system, etc.
            }),
        ),
    )

    s.Start()
    select {}
}

Graceful Shutdown

import (
    "context"
    "os"
    "os/signal"
    "syscall"
)

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

    s.NewJob(
        gocron.DurationJob(time.Minute),
        gocron.NewTask(func(ctx context.Context) {
            fmt.Println("Long-running task started")

            for i := 0; i < 60; i++ {
                select {
                case <-ctx.Done():
                    fmt.Println("Task cancelled, cleaning up...")
                    cleanup()
                    return
                default:
                    time.Sleep(time.Second)
                    fmt.Printf("Progress: %d/60\n", i+1)
                }
            }

            fmt.Println("Task completed")
        }),
        gocron.WithName("long-task"),
    )

    s.Start()

    // Wait for interrupt
    ctx, cancel := signal.NotifyContext(
        context.Background(),
        os.Interrupt,
        syscall.SIGTERM,
    )
    defer cancel()

    <-ctx.Done()
    fmt.Println("Received shutdown signal, stopping scheduler...")

    if err := s.Shutdown(); err != nil {
        fmt.Printf("Shutdown error: %v\n", err)
    }

    fmt.Println("Shutdown complete")
}

func cleanup() {
    fmt.Println("Cleaning up resources...")
}

Job Status Tracking

type jobTracker struct {
    jobs map[uuid.UUID]*jobInfo
    mu   sync.Mutex
}

type jobInfo struct {
    Name       string
    LastRun    time.Time
    LastStatus string
    RunCount   int
}

func newJobTracker() *jobTracker {
    return &jobTracker{
        jobs: make(map[uuid.UUID]*jobInfo),
    }
}

func (t *jobTracker) JobScheduled(jobID uuid.UUID, job gocron.Job) {
    t.mu.Lock()
    defer t.mu.Unlock()

    t.jobs[jobID] = &jobInfo{
        Name:       job.Name(),
        LastStatus: "scheduled",
    }
}

func (t *jobTracker) JobStarted(jobID uuid.UUID, job gocron.Job) {
    t.mu.Lock()
    defer t.mu.Unlock()

    if info, ok := t.jobs[jobID]; ok {
        info.LastStatus = "running"
        info.LastRun = time.Now()
    }
}

func (t *jobTracker) JobCompleted(jobID uuid.UUID, job gocron.Job, err error) {
    t.mu.Lock()
    defer t.mu.Unlock()

    if info, ok := t.jobs[jobID]; ok {
        info.RunCount++
        if err != nil {
            info.LastStatus = "failed"
        } else {
            info.LastStatus = "completed"
        }
    }
}

func (t *jobTracker) JobUnscheduled(jobID uuid.UUID, job gocron.Job) {
    t.mu.Lock()
    defer t.mu.Unlock()

    delete(t.jobs, jobID)
}

func (t *jobTracker) ConcurrencyLimitReached(limitType string, job gocron.Job) {}

func (t *jobTracker) PrintStatus() {
    t.mu.Lock()
    defer t.mu.Unlock()

    fmt.Println("\n=== Job Status ===")
    for jobID, info := range t.jobs {
        fmt.Printf("%s: %s (runs: %d, last: %v)\n",
            info.Name, info.LastStatus, info.RunCount, info.LastRun)
    }
    fmt.Println()
}

func main() {
    tracker := newJobTracker()

    s, _ := gocron.NewScheduler(
        gocron.WithSchedulerMonitor(tracker),
    )
    defer s.Shutdown()

    s.NewJob(
        gocron.DurationJob(5*time.Second),
        gocron.NewTask(func() {
            time.Sleep(time.Second)
        }),
        gocron.WithName("worker-1"),
    )

    s.NewJob(
        gocron.DurationJob(10*time.Second),
        gocron.NewTask(func() error {
            if time.Now().Unix()%2 == 0 {
                return fmt.Errorf("error")
            }
            return nil
        }),
        gocron.WithName("worker-2"),
    )

    s.Start()

    // Print status every 15 seconds
    ticker := time.NewTicker(15 * time.Second)
    defer ticker.Stop()

    go func() {
        for range ticker.C {
            tracker.PrintStatus()
        }
    }()

    time.Sleep(time.Minute)
}

One-Time Initialization

func main() {
    s, _ := gocron.NewScheduler()
    defer s.Shutdown()

    // Run initialization immediately
    initJob, _ := s.NewJob(
        gocron.OneTimeJob(
            gocron.OneTimeJobStartDateTime(time.Now()),
        ),
        gocron.NewTask(func() {
            fmt.Println("Initializing system...")
            time.Sleep(2 * time.Second)
            fmt.Println("Initialization complete")
        }),
        gocron.WithName("init"),
    )

    // Regular jobs start after init
    s.NewJob(
        gocron.DurationJob(time.Minute),
        gocron.NewTask(func() {
            fmt.Println("Regular task running")
        }),
        gocron.WithName("regular-task"),
    )

    s.Start()

    // Wait for initialization
    time.Sleep(3 * time.Second)

    fmt.Println("All systems ready")
    select {}
}

See Also

  • Lifecycle Guide
  • Graceful Shutdown Guide
  • Event Options API

Install with Tessl CLI

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

docs

index.md

tile.json