How to Set Up Cron Jobs Using the Cronx Module in Gorig
Use the cronx package in jom-io/gorig to register scheduled tasks via AddCronTask, AddEveryTask, or AddOnceTask, then start the scheduler with Startup and stop it gracefully with Shutdown.
The cronx module provides Gorig’s official abstraction for background job scheduling, wrapping the robust robfig/cron library with framework-specific lifecycle management. When you set up cron jobs using the cronx module in Gorig, you gain built-in context propagation, panic recovery, timeout enforcement, and graceful shutdown handling that integrates seamlessly with the rest of the Gorig ecosystem.
Understanding the Cronx Module Architecture
The cronx package is a thin but opinionated wrapper located in cronx/cron.go. It manages a singleton cron.Cron instance and exposes registration helpers that enforce Gorig’s standards for observability and safety.
Key architectural features include:
- Context-aware execution – Every task receives a
context.Contextderived from the job’s timeout or cancellation signal. - Panic recovery – The wrapper in
cronx/task.goautomatically recovers from panics, logs the stack trace, and prevents the scheduler from crashing. - Timeout enforcement – Optional timeout parameters create cancellable goroutines that respect deadlines.
- Lifecycle hooks –
StartupandShutdownmethods follow Gorig’s standard service pattern, enabling clean initialization and termination.
Registering Cron Jobs in Gorig
All task registration must occur before calling Startup. The module offers four primary registration functions, each defined in cronx/cron.go.
Recurring Tasks with Cron Expressions
Use AddCronTask when you need standard cron syntax (@every 1s, 0 0 * * *, etc.).
// Runs every 5 minutes with a 30-second timeout
cronx.AddCronTask("*/5 * * * *", func(ctx context.Context) {
// Your business logic here
processData(ctx)
}, 30*time.Second)
Interval-Based Execution
AddEveryTask provides a more readable API for fixed intervals without writing cron expressions.
// Executes every 10 seconds
cronx.AddEveryTask(10*time.Second, func(ctx context.Context) {
cleanupTempFiles(ctx)
}, 5*time.Second) // 5s timeout
Delayed and One-Off Tasks
For non-recurring workloads, use AddDelayTask or AddOnceTask.
// Run once after a 1-minute delay
cronx.AddDelayTask(1*time.Minute, func(ctx context.Context) {
sendWelcomeEmail(ctx)
}, 10*time.Second)
// Run once at a specific time
futureTime := time.Now().Add(30 * time.Minute)
cronx.AddOnceTask(futureTime, func(ctx context.Context) {
generateNightlyReport(ctx)
})
Note: The older AddTask function remains available for backward compatibility but is marked as deprecated in cronx/cron.go because it lacks context support and panic recovery.
Starting and Managing the Scheduler
Initializing with Startup
The Startup function in cronx/cron.go begins processing registered jobs. It accepts a service identifier and an optional HTTP port for health checks.
// Start cron service without HTTP endpoint
if err := cronx.Startup("DATA_SYNC", ""); err != nil {
log.Fatalf("cron startup failed: %v", err)
}
// Or with health endpoint on port 8081
if err := cronx.Startup("DATA_SYNC", "8081"); err != nil {
log.Fatal(err)
}
Graceful Shutdown Handling
To prevent data loss or corruption, always call Shutdown when the application receives a termination signal. This method stops the scheduler, waits for running jobs to complete (respecting the provided context), and cleans up resources.
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := cronx.Shutdown("DATA_SYNC", ctx); err != nil {
log.Printf("cron shutdown error: %v", err)
}
Production-Ready Cron Job Examples
Basic Recurring Task
This example demonstrates a minimal setup that prints a timestamp every 5 seconds.
package main
import (
"context"
"log"
"time"
"github.com/jom-io/gorig/cronx"
)
func main() {
cronx.AddCronTask("@every 5s", func(ctx context.Context) {
log.Println("tick at", time.Now())
})
if err := cronx.Startup("HEARTBEAT", ""); err != nil {
log.Fatalf("failed to start cron: %v", err)
}
select {} // Block forever
}
Task with Timeout and Panic Recovery
The following job simulates unstable work that occasionally panics. The cronx wrapper in cronx/task.go automatically recovers and logs the error without crashing the scheduler.
cronx.AddCronTask("@every 10s", func(ctx context.Context) {
// Simulate work that might panic
if time.Now().Unix()%2 == 0 {
panic("simulated failure")
}
log.Println("completed successfully")
}, 2*time.Second) // 2-second timeout
Scheduled One-Off Task
Use AddOnceTask to schedule maintenance windows or delayed notifications.
runAt := time.Now().Add(30 * time.Minute)
cronx.AddOnceTask(runAt, func(ctx context.Context) {
log.Println("Generating nightly report at", time.Now())
generateReport(ctx)
})
Graceful Shutdown Implementation
Integrate with Go’s signal handling for production deployments.
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := cronx.Shutdown("WORKER", ctx); err != nil {
log.Printf("cron shutdown error: %v", err)
}
Key Source Files in the Cronx Module
Understanding the internal structure helps when debugging or extending functionality.
| File | Purpose | Location |
|---|---|---|
cronx/cron.go |
Public API surface including AddCronTask, Startup, and Shutdown |
github.com/jom-io/gorig/cronx/cron.go |
cronx/task.go |
Internal Task struct definition, timeout enforcement, and panic recovery wrapper |
github.com/jom-io/gorig/cronx/task.go |
cronx/logger.go |
Gorig-standard logger configuration for cron output | github.com/jom-io/gorig/cronx/logger.go |
test/cron_test.go |
Unit tests demonstrating valid usage patterns | github.com/jom-io/gorig/test/cron_test.go |
Summary
- The
cronxmodule injom-io/gorigwrapsrobfig/cronto provide context-aware, panic-safe job scheduling. - Register tasks before startup using
AddCronTask(cron expressions),AddEveryTask(intervals),AddDelayTask(delayed), orAddOnceTask(one-time). - Start the scheduler with
Startup(serviceName, healthPort)and stop it gracefully withShutdown(serviceName, ctx). - All tasks receive a
context.Contextand support optional timeouts and automatic panic recovery via the wrapper incronx/task.go.
Frequently Asked Questions
What is the difference between AddCronTask and AddEveryTask?
AddCronTask accepts standard cron expressions like @every 1s or 0 0 * * *, while AddEveryTask takes a time.Duration directly (e.g., 10*time.Second) for simpler interval-based scheduling. Both functions support context propagation and timeout parameters, but AddEveryTask eliminates the need to parse cron syntax for simple recurring intervals.
How does cronx handle panics in scheduled jobs?
The cronx module automatically recovers from panics via the wrapper logic defined in cronx/task.go. When a task panics, the wrapper captures the error, logs it using the Gorig logger, and allows the scheduler to continue running subsequent jobs. This prevents a single faulty task from crashing the entire cron service.
Can I run a task only once at a specific time?
Yes, use the AddOnceTask function to schedule a one-time execution. Pass a time.Time value indicating when the task should run, along with your handler function. The task will execute exactly once at the specified time and then automatically remove itself from the scheduler.
What happens if I don't call Shutdown before the application exits?
If Shutdown is not called, the underlying robfig/cron scheduler may terminate abruptly, potentially interrupting running jobs and causing data loss or inconsistent state. Always call Shutdown with a context timeout during application termination (e.g., in signal handlers) to allow in-flight tasks to complete gracefully.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →