# Packages
# README
Cron
A Go library for robust cron job scheduling with multiple execution strategies, observability, and reliability features.
Installation
go get github.com/bborbe/cron
Quick Start
Basic Usage
package main
import (
"context"
"fmt"
"github.com/bborbe/cron"
"github.com/bborbe/run"
)
func main() {
ctx := context.Background()
// Create an action to run
action := run.Func(func(ctx context.Context) error {
fmt.Println("Job executed!")
return nil
})
// Expression-based scheduling
cronJob := cron.NewCronJob(false, cron.Expression("@every 1h"), 0, action)
cronJob.Run(ctx)
}
Enhanced Usage with Options
package main
import (
"context"
"time"
"github.com/bborbe/cron"
"github.com/bborbe/run"
)
func main() {
ctx := context.Background()
action := run.Func(func(ctx context.Context) error {
// Your job logic here
return nil
})
// Configure options
options := cron.CronJobOptions{
Name: "my-important-job",
EnableMetrics: true,
Timeout: 5 * time.Minute,
ParallelSkip: true,
}
// Create job with options
cronJob := cron.NewCronJobWithOptions(
false, // not one-time
cron.Expression("0 */15 * * * ?"), // every 15 minutes
0, // no wait duration
action,
options,
)
cronJob.Run(ctx)
}
Execution Strategies
The library automatically selects the appropriate execution strategy based on the parameters provided to NewCronJob
or NewCronJobWithOptions
:
1. Expression-Based (Cron Expressions)
Uses standard cron expressions or special formats:
// Every hour
cronJob := cron.NewCronJob(false, cron.Expression("@every 1h"), 0, action)
// Every 15 minutes using cron syntax
cronJob := cron.NewCronJob(false, cron.Expression("0 */15 * * * ?"), 0, action)
// Daily at 2:30 AM
cronJob := cron.NewCronJob(false, cron.Expression("0 30 2 * * ?"), 0, action)
2. Duration-Based (Intervals)
Uses Go time.Duration
for simple intervals:
// Every 30 seconds
cronJob := cron.NewCronJob(false, "", 30*time.Second, action)
// Every 5 minutes
cronJob := cron.NewCronJob(false, "", 5*time.Minute, action)
3. One-Time Execution
Executes the job once immediately:
// Run once
cronJob := cron.NewCronJob(true, "", 0, action)
Configuration Options
The CronJobOptions
struct provides fine-grained control over job behavior:
type CronJobOptions struct {
Name string // Job name for metrics and logging
EnableMetrics bool // Enable Prometheus metrics collection
Timeout time.Duration // Execution timeout (0 = disabled)
ParallelSkip bool // Prevent concurrent executions
}
Default Options
options := cron.DefaultCronJobOptions()
// Returns:
// {
// Name: "unnamed-cron",
// EnableMetrics: false,
// Timeout: 0,
// ParallelSkip: false,
// }
Wrapper Functions
For maximum flexibility, you can use wrapper functions directly:
Metrics Wrapper
import "github.com/prometheus/client_golang/prometheus"
// Wrap any action with metrics
wrappedAction := cron.WrapWithMetrics("job-name", originalAction)
Metrics Collected:
cron_job_started{name="job-name"}
- Number of job startscron_job_completed{name="job-name"}
- Number of successful completionscron_job_failed{name="job-name"}
- Number of failurescron_job_last_success{name="job-name"}
- Timestamp of last success
Timeout Wrapper
// Add 5-minute timeout
wrappedAction := cron.WrapWithTimeout("job-name", 5*time.Minute, originalAction)
// Disable timeout (≤0 duration)
wrappedAction := cron.WrapWithTimeout("job-name", 0, originalAction)
Chaining Wrappers
// Chain multiple wrappers (order matters)
action := cron.WrapWithTimeout("my-job", 5*time.Minute, originalAction)
action = cron.WrapWithMetrics("my-job", action)
Monitoring and Observability
Prometheus Integration
When metrics are enabled, the library automatically registers Prometheus collectors. Expose them via HTTP:
import (
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
// Your cron jobs with EnableMetrics: true
// Expose metrics endpoint
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8080", nil)
}
Custom Metrics
Access the metrics interface directly:
metrics := cron.NewCronMetrics()
metrics.IncreaseStarted("custom-job")
metrics.IncreaseCompleted("custom-job")
metrics.SetLastSuccessToCurrent("custom-job")
Error Handling
The library provides proper error propagation through all wrapper layers:
action := run.Func(func(ctx context.Context) error {
return errors.New("job failed")
})
cronJob := cron.NewCronJobWithOptions(true, "", 0, action, options)
err := cronJob.Run(ctx)
if err != nil {
// Handle error - will include context from wrappers
log.Printf("Cron job failed: %v", err)
}
Testing
The library provides mock interfaces for testing:
//go:generate go run -mod=mod github.com/maxbrunsfeld/counterfeiter/v6 -generate
import "github.com/bborbe/cron/mocks"
func TestMyCronJob(t *testing.T) {
mockCronJob := &mocks.CronJob{}
mockMetrics := &mocks.CronMetrics{}
// Set up expectations and test your code
}
Examples
Web Scraper with Monitoring
options := cron.CronJobOptions{
Name: "web-scraper",
EnableMetrics: true,
Timeout: 30 * time.Second,
ParallelSkip: true,
}
scraper := run.Func(func(ctx context.Context) error {
// Scrape website logic
return scrapeWebsite(ctx)
})
cronJob := cron.NewCronJobWithOptions(
false,
cron.Expression("0 */5 * * * ?"), // Every 5 minutes
0,
scraper,
options,
)
Database Cleanup Job
cleanup := run.Func(func(ctx context.Context) error {
return cleanupOldRecords(ctx)
})
options := cron.CronJobOptions{
Name: "db-cleanup",
EnableMetrics: true,
Timeout: 10 * time.Minute,
}
cronJob := cron.NewCronJobWithOptions(
false,
cron.Expression("0 0 2 * * ?"), // Daily at 2 AM
0,
cleanup,
options,
)
License
BSD-style license. See LICENSE file for details.