Note
This README was translated by ChatGPT 4o
Lightweight Golang scheduler supporting standard cron expressions, custom descriptors, custom intervals, and task dependencies. Easy scheduling with Go
Originally designed for the scheduling functionality used in threat score decay calculations for pardnchiu/go-ip-sentry
- Core Features
- Flowchart
- Dependencies
- Usage
- Configuration
- Supported Formats
- Available Functions
- Task Dependencies
- Timeout Mechanism
- Upcoming Features
- License
- Star
- Author
Supports standard cron expressions, custom descriptors (@hourly
, @daily
, @weekly
, etc.), and custom interval (@every
) syntax. Zero learning curve - if you know cron expressions, you can use it.
Supports prerequisite dependency tasks, multiple dependencies, dependency timeout control, and failure handling mechanisms.
Uses Golang standard library heap
, focusing on core functionality. Min-heap based task scheduling with concurrent task execution and management, featuring panic recovery and dynamic task add/remove capabilities, ensuring optimal performance in high-volume task scenarios.
Main Process
flowchart TD
A[Initialize] --> B{Is Running?}
B -->|No| B0[Start Execution]
B0 --> C[Calculate Initial Tasks]
C --> D[Initialize Tasks]
D --> E[Start Main Loop]
E --> H{Check Heap Status}
E -->|No Tasks<br>Wait for Events| Q
E -->|Has Tasks<br>Set Next Task Timer| Q
B -->|Yes<br>Wait for Trigger| Q[Listen Events]
Q --> R{Event Type}
R -->|Timer Expired| R1[Execute Due Tasks]
R -->|Add Task| R2[Add to Heap]
R -->|Remove Task| R3[Remove from Heap]
R -->|Stop Signal| R4[Cleanup and Exit]
R1 --> S[Pop Task from Heap]
S --> R5[Calculate Next Execution Time]
R5 --> E
S --> T{Check if Enabled}
T -->|Disabled| T0[Skip Task]
T0 --> E
T -->|Enabled| T1[Execute Task Function]
R2 --> V[Parse Schedule]
V --> W[Create Task Object]
W --> X[Add to Heap]
R3 --> Y[Find Task by ID]
Y --> Z[Mark as Disabled]
Z --> AA[Remove from Heap]
X --> E
AA --> E
R4 --> BB[Wait for Running Tasks to Complete]
BB --> CC[Close Channels]
CC --> DD[Scheduler Stopped]
Dependency Process
flowchart TD
A[Task Added to Execution Queue] --> B{Check Dependencies}
B -->|No Dependencies| B0[Skip Dependency Process]
B0 --> Z[End]
B -->|Has Dependencies| B1{Dependencies Complete?}
B1 -->|No| B10[Wait for Dependencies]
B10 --> C{Dependency Wait Timeout?}
C -->|No| C0[Continue Waiting]
C0 --> D{Dependency Resolved?}
D -->|Failed<br>Mark Failed| V
D -->|Completed| B11
D -->|Still Waiting| B10
C -->|Yes<br>Mark Failed| V
B1 -->|Yes| B11[Execute]
B11 -->|Mark Running| E{Task Timeout Exists?}
E -->|No| E0[Execute Action]
E0 --> R{Execution Result}
R -->|Success<br>Mark Complete| V[Update Task Result]
R -->|Error<br>Mark Failed| V
R -->|Panic<br>Recover and Mark Failed| V
E -->|Yes| E1{Task Timeout?}
E1 -->|Timeout<br>Mark Failed<br>Trigger Timeout Action| V
E1 -->|Not Timeout| E0
B1 -->|Failed<br>Mark Failed| V
V --> X[Record Execution Result]
X --> Y[Notify Dependent Tasks]
Y --> Z[End]
(< v0.3.1)github.com/pardnchiu/go-logger
For performance and stability, non-standard library packages are deprecated fromv0.3.1
, now usinglog/slog
Note
Latest commit may change, recommended to use tagged versions
Commits containing only documentation updates or non-functional changes will be rebased later
go get github.com/pardnchiu/go-cron@[VERSION]
git clone --depth 1 --branch [VERSION] https://github.com/pardnchiu/go-cron.git
package main
import (
"fmt"
"log"
"time"
cron "github.com/pardnchiu/go-cron"
)
func main() {
// Initialize (optional configuration)
scheduler, err := cron.New(cron.Config{
Location: time.Local,
})
if err != nil {
log.Fatal(err)
}
// Start scheduler
scheduler.Start()
// Add tasks
id1, _ := scheduler.Add("@daily", func() {
fmt.Println("Daily execution")
}, "Backup task")
id2, _ := scheduler.Add("@every 5m", func() {
fmt.Println("Execute every 5 minutes")
})
// View task list
tasks := scheduler.List()
fmt.Printf("Currently have %d tasks\n", len(tasks))
// Remove specific task
scheduler.Remove(id1)
// Remove all tasks
scheduler.RemoveAll()
// Graceful shutdown
ctx := scheduler.Stop()
<-ctx.Done()
}
package main
import (
"fmt"
"log"
"time"
cron "github.com/pardnchiu/go-cron"
)
func main() {
scheduler, err := cron.New(cron.Config{})
if err != nil {
log.Fatal(err)
}
scheduler.Start()
defer func() {
ctx := scheduler.Stop()
<-ctx.Done()
}()
// Task A: Data preparation
taskA, _ := scheduler.Add("0 1 * * *", func() error {
fmt.Println("Preparing data...")
time.Sleep(2 * time.Second)
return nil
}, "Data preparation")
// Task B: Data processing
taskB, _ := scheduler.Add("0 2 * * *", func() error {
fmt.Println("Processing data...")
time.Sleep(3 * time.Second)
return nil
}, "Data processing")
// Task C: Report generation (depends on A and B)
taskC, _ := scheduler.Add("0 3 * * *", func() error {
fmt.Println("Generating report...")
time.Sleep(1 * time.Second)
return nil
}, "Report generation", []Wait{{ID: taskA}, {ID: taskB}})
// Task D: Email sending (depends on C)
_, _ = scheduler.Add("0 4 * * *", func() error {
fmt.Println("Sending email...")
return nil
}, "Email notification", []Wait{{ID: taskC}})
time.Sleep(10 * time.Second)
}
type Config struct {
Location *time.Location // Timezone setting (default: time.Local)
}
5-field format:
minute hour day month weekday
Supports range syntax1-5
and1,3,5
// Every minute
scheduler.Add("* * * * *", task)
// Daily at midnight
scheduler.Add("0 0 * * *", task)
// Every 15 minutes
scheduler.Add("*/15 * * * *", task)
// First day of month at 6 AM
scheduler.Add("0 6 1 * *", task)
// Monday to Wednesday, and Friday
scheduler.Add("0 0 * * 1-3,5", task)
// January 1st at midnight
scheduler.Add("@yearly", task)
// First day of month at midnight
scheduler.Add("@monthly", task)
// Every Sunday at midnight
scheduler.Add("@weekly", task)
// Daily at midnight
scheduler.Add("@daily", task)
// Every hour on the hour
scheduler.Add("@hourly", task)
// Every 30 seconds (minimum interval: 30 seconds)
scheduler.Add("@every 30s", task)
// Every 5 minutes
scheduler.Add("@every 5m", task)
// Every 2 hours
scheduler.Add("@every 2h", task)
// Every 12 hours
scheduler.Add("@every 12h", task)
-
New()
- Create new scheduler instancescheduler, err := cron.New(config)
- Sets up task heap and communication channels
-
Start()
- Start scheduler instancescheduler.Start()
- Starts the scheduling loop
-
Stop()
- Stop schedulerctx := scheduler.Stop() <-ctx.Done() // Wait for all tasks to complete
- Sends stop signal to main loop
- Returns context that completes when all running tasks finish
- Ensures graceful shutdown without interrupting tasks
-
Add()
- Add scheduled task// Basic usage (no return value) taskID, err := scheduler.Add("0 */2 * * *", func() { // Task logic }) // Task with error return (supports dependencies) taskID, err := scheduler.Add("@daily", func() error { // Task logic return nil }, "Backup task") // Task with timeout control taskID, err := scheduler.Add("@hourly", func() error { // Long-running task time.Sleep(10 * time.Second) return nil }, "Data processing", 5*time.Second) // Task with timeout callback taskID, err := scheduler.Add("@daily", func() error { // Potentially timeout-prone task return heavyProcessing() }, "Critical backup", 30*time.Second, func() { log.Println("Backup task timed out, please check system status") }) // Task with dependencies taskID, err := scheduler.Add("@daily", func() error { // Task that depends on other tasks return processData() }, "Data processing", []Wait{{ID: taskA}, {ID: taskB}}) // Task with dependencies and timeout taskID, err := scheduler.Add("@daily", func() error { return generateReport() }, "Report generation", []Wait{ {ID: taskA, Delay: 30 * time.Second}, {ID: taskB, Delay: 45 * time.Second}, })
- Parses schedule syntax
- Generates unique task ID for management
- Supports variadic parameter configuration
string
: Task descriptiontime.Duration
: Task execution timeoutfunc()
: Callback function triggered on timeout[]Wait
: Dependency task configuration (recommended format)[]int64
: Dependency task ID list (will be removed after v2.0)
- Supports two action function types
func()
: No error return, doesn't support dependenciesfunc() error
: Has error return, supports dependencies
-
Remove()
- Cancel task schedulescheduler.Remove(taskID)
- Removes task from scheduling queue
- Safe to call regardless of scheduler state
-
RemoveAll()
- Remove all tasksscheduler.RemoveAll()
- Immediately removes all scheduled tasks
- Doesn't affect currently running tasks
-
List()
- Get task listtasks := scheduler.List()
- No dependencies: Execute directly
- Has dependencies: Execute through worker pool and dependency manager
- Single dependency: Task B executes after Task A completes
- Multiple dependencies: Task C waits for both Task A and B to complete
- Dependency task timeout: Maximum time to wait for dependency completion (default 1 minute)
Failure handling strategies:
// Skip: Continue execution when dependency fails
taskC, _ := scheduler.Add("0 3 * * *", func() error {
fmt.Println("Generating report...")
return nil
}, "Report generation", []Wait{
{ID: taskA, State: Skip}, // Skip when taskA fails
{ID: taskB, State: Stop}, // Stop when taskB fails (default)
})
Custom timeout:
// Set independent wait time for each dependency
taskC, _ := scheduler.Add("0 3 * * *", func() error {
fmt.Println("Generating report...")
return nil
}, "Report generation", []Wait{
{ID: taskA, Delay: 30 * time.Second}, // Wait 30 seconds
{ID: taskB, Delay: 45 * time.Second}, // Wait 45 seconds
})
Combined usage:
// Combine failure strategies with custom timeout
taskC, _ := scheduler.Add("0 3 * * *", func() error {
fmt.Println("Generating report...")
return nil
}, "Report generation", []Wait{
{ID: taskA, Delay: 30 * time.Second, State: Skip},
{ID: taskB, Delay: 45 * time.Second, State: Stop},
})
const (
TaskPending // Waiting
TaskRunning // Running
TaskCompleted // Completed
TaskFailed // Failed / Timeout
)
When execution time exceeds the set Delay
:
- Interrupts task execution
- Triggers
OnDelay
function (if set) - Logs timeout
- Continues with next schedule
- Timeout implemented using
context.WithTimeout
- Timeout doesn't affect other task execution
- If action completes before timeout, timeout won't trigger
- Status callbacks: Add
OnTimeout
andOnFailed
callback functions for monitoring and responding to dependency task exception states
- Event-driven: Replace current polling with fully
channel
-based approach to reduce CPU usage - Dependency awakening: Implement active notification mechanism when dependency tasks complete, eliminating ineffective polling checks
This project is licensed under MIT.
©️ 2025 邱敬幃 Pardn Chiu