gocron

repository·v2·Indexed 27 days ago

https://github.com/go-co-op/gocron

A Golang job scheduling package that allows developers to run Go functions at pre-determined intervals. It supports various timing strategies including Cron, Duration, Daily, Weekly, Monthly, and One-time schedules. The library features concurrency limits (singleton and limit modes), distributed instance support via Elector and Locker interfaces, and observability through the SchedulerMonitor for tracking job lifecycles and performance metrics.

Tokens
11.2K
Snippets
21
Records
51
Agent score
90%

What's inside gocron

  1. Core Concepts of gocron

    v2

    Understanding the three main components of gocron:

    • Job: Encapsulates a "task" (a Go function and its parameters) and provides the scheduler with the next scheduled run time.
    • Scheduler: Manages all jobs and dispatches them to the executor when they are ready to run.
    • Executor: Executes the job's task and manages execution complexities like concurrency limits and singleton modes.
  2. Quick Start with gocron

    v2

    This example demonstrates how to initialize a scheduler, add a duration-based job with a task, start the scheduler, and perform a graceful shutdown.

    package main
    
    import (
    	"fmt"
    	"time"
    
    	"github.com/go-co-op/gocron/v2"
    )
    
    func main() {
    	// create a scheduler
    	s, err := gocron.NewScheduler()
    	if err != nil {
    		// handle error
    	}
    
    	// add a job to the scheduler
    	j, err := s.NewJob(
    		gocron.DurationJob(
    			10*time.Second,
    		),
    		gocron.NewTask(
    			func(a string, b int) {
    				// do things
    			},
    			"hello",
    			1,
    		),
    	)
    	if err != nil {
    		// handle error
    	}
    	// each job has a unique id
    	fmt.Println(j.ID())
    
    	// start the scheduler
    	s.Start()
    
    	// block until you are ready to shut down
    	select {
    	case <-time.After(time.Minute):
    	}
    
    	// when you're done, shut it down
    	err = s.Shutdown()
    	// or for context-aware teardown:
    	// err = s.ShutdownWithContext(ctx)
    	if err != nil {
    		// handle error
    	}
    }
  3. Test gocron with Mocks and FakeClock

    v2

    The library is designed for testability:

    • Mocks: Use the provided mocks package (built with gomock) for testing.
    • Time Mocking: Pass a FakeClock (from github.com/jonboulle/clockwork) to the WithClock option to control time in your tests.
  4. Mock the gocron Scheduler for testing

    v2

    You can use gocronmocks.NewMockScheduler along with uber-go/mock (gomock) to create a mock implementation of the gocron.Scheduler interface. This allows you to verify that methods like Start() and Shutdown() are called with the expected frequency and return values.

    package main
    
    import (
    	"testing"
    
    	"github.com/go-co-op/gocron/mocks/v2"
    	"github.com/go-co-op/gocron/v2"
    	"go.uber.org/mock/gomock"
    )
    
    func myFunc(s gocron.Scheduler) {
    	s.Start()
    	_ = s.Shutdown()
    }
    
    func TestMyFunc(t *testing.T) {
    	ctrl := gomock.NewController(t)
    	s := gocronmocks.NewMockScheduler(ctrl)
    	s.EXPECT().Start().Times(1)
    	s.EXPECT().Shutdown().Times(1).Return(nil)
    
    	myFunc(s)
    }
  5. Monitor gocron with SchedulerMonitor

    v2

    The SchedulerMonitor provides observability into scheduler and job lifecycle events. You can implement this interface to collect metrics for Prometheus, custom dashboards, or alerting systems.

    Available Metrics:

    • Scheduler Lifecycle: SchedulerStarted, SchedulerStopped, SchedulerShutdown
    • Job Management: JobRegistered, JobUnregistered
    • Job Execution: JobStarted, JobRunning, JobCompleted, JobFailed
    • Performance: JobExecutionTime, JobSchedulingDelay
    • Concurrency: ConcurrencyLimitReached

    Example - Prometheus Integration:

    type PrometheusMonitor struct {
        jobsCompleted   prometheus.Counter
        jobsFailed      prometheus.Counter
        executionTime   prometheus.Histogram
        schedulingDelay prometheus.Histogram
    }
    
    func (p *PrometheusMonitor) JobExecutionTime(job gocron.Job, duration time.Duration) {
        p.executionTime.Observe(duration.Seconds())
    }
    
    func (p *PrometheusMonitor) JobSchedulingDelay(job gocron.Job, scheduled, actual time.Time) {
        if delay := actual.Sub(scheduled); delay > 0 {
            p.schedulingDelay.Observe(delay.Seconds())
        }
    }
    
    // Initialize scheduler with monitor
    s, _ := gocron.NewScheduler(gocron.WithSchedulerMonitor(monitor))
  6. Create a new scheduler in v2

    v2

    In v2, use gocron.NewScheduler(). Unlike v1, this function returns an error and does not require a timezone argument by default. To specify a location, use the WithLocation() option.

    import "github.com/go-co-op/gocron/v2"
    
    s, err := gocron.NewScheduler()
    if err != nil { panic(err) }
  7. Create jobs in v2

    v2

    v2 replaces the fluent API (e.g., .Every().Second()) with explicit job types and the NewJob method. You must provide a job type (like DurationJob or CronJob) and a task created via NewTask. Jobs in v2 return an error on creation and provide unique IDs via j.ID().

    // Duration-based job
    j, err := s.NewJob(
        gocron.DurationJob(1*time.Second),
        gocron.NewTask(taskFunc),
    )
    if err != nil { panic(err) }
    
    // Cron-based job
    j, err := s.NewJob(
        gocron.CronJob("*/5 * * * *"),
        gocron.NewTask(taskFunc),
    )
    if err != nil { panic(err) }
    
    // Job with arguments
    j, err := s.NewJob(
        gocron.DurationJob(1*time.Second),
        gocron.NewTask(taskFunc, arg1, arg2),
    )
    if err != nil { panic(err) }
  8. Use job tags and identifiers

    v2

    Assign metadata to jobs for easier management:

    • WithName(name string): Sets a human-readable name for the job.
    • WithTags(tags ...string): Assigns a set of tags, allowing you to identify or remove multiple jobs by tag.
    • WithIdentifier(id uuid.UUID): Sets a unique UUID for the job, used for logging and metrics.
  9. Configure job execution limits and modes

    v2

    Use these options to control how many times a job runs or how it behaves if it overlaps with itself:

    • WithLimitedRuns(limit uint): Limits the job to exactly $N$ executions, after which it is removed from the scheduler.
    • WithSingletonMode(mode LimitMode): Prevents a job from running if it is already running. This is useful for preventing overlapping executions.