https://github.com/pardnchiu/go-cron
A lightweight Golang scheduler supporting standard cron expressions, custom descriptors, custom intervals, and task dependencies. Easily write schedules using Go.
https://github.com/pardnchiu/go-cron
cron cron-scheduler golang pardnchiu scheduler srp standard-library task-dependencies task-scheduler
Last synced: 5 months ago
JSON representation
A lightweight Golang scheduler supporting standard cron expressions, custom descriptors, custom intervals, and task dependencies. Easily write schedules using Go.
- Host: GitHub
- URL: https://github.com/pardnchiu/go-cron
- Owner: pardnchiu
- License: mit
- Created: 2025-06-22T12:49:07.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2025-07-28T13:38:58.000Z (6 months ago)
- Last Synced: 2025-08-01T10:56:04.791Z (5 months ago)
- Topics: cron, cron-scheduler, golang, pardnchiu, scheduler, srp, standard-library, task-dependencies, task-scheduler
- Language: Go
- Homepage:
- Size: 67.4 KB
- Stars: 31
- Watchers: 2
- Forks: 6
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-go - pardnchiu/go-cron
README
> [!NOTE]
> This README was translated by ChatGPT 4o
# Cron Scheduler
> 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](https://github.com/pardnchiu/go-ip-sentry)
[](https://pkg.go.dev/github.com/pardnchiu/go-cron)
[](https://goreportcard.com/report/github.com/pardnchiu/go-cron)
[](https://app.codecov.io/github/pardnchiu/go-cron)
[](https://github.com/pardnchiu/go-cron/releases)
[](LICENSE)
[](README.md)
[](README.zh.md)
- [Core Features](#core-features)
- [Flexible Syntax](#flexible-syntax)
- [Task Dependencies](#task-dependencies)
- [Efficient Architecture](#efficient-architecture)
- [Flowchart](#flowchart)
- [Dependencies](#dependencies)
- [Usage](#usage)
- [Installation](#installation)
- [Initialization](#initialization)
- [Basic Usage](#basic-usage)
- [Task Dependencies](#task-dependencies-1)
- [Configuration](#configuration)
- [Supported Formats](#supported-formats)
- [Standard](#standard)
- [Custom](#custom)
- [Available Functions](#available-functions)
- [Scheduler Management](#scheduler-management)
- [Task Management](#task-management)
- [Task Dependencies](#task-dependencies-2)
- [Basic Usage](#basic-usage-1)
- [Dependency Examples](#dependency-examples)
- [Task Status](#task-status)
- [Timeout Mechanism](#timeout-mechanism)
- [Features](#features)
- [Upcoming Features](#upcoming-features)
- [Enhanced Task Dependencies](#enhanced-task-dependencies)
- [Task Completion Trigger Refactor](#task-completion-trigger-refactor)
- [License](#license)
- [Star](#star)
- [Author](#author)
## Core Features
### Flexible Syntax
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.
### Task Dependencies
Supports prerequisite dependency tasks, multiple dependencies, dependency timeout control, and failure handling mechanisms.
### Efficient Architecture
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.
## Flowchart
Main Process
```mermaid
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
Wait for Events| Q
E -->|Has Tasks
Set Next Task Timer| Q
B -->|Yes
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
```mermaid
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
Mark Failed| V
D -->|Completed| B11
D -->|Still Waiting| B10
C -->|Yes
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
Mark Complete| V[Update Task Result]
R -->|Error
Mark Failed| V
R -->|Panic
Recover and Mark Failed| V
E -->|Yes| E1{Task Timeout?}
E1 -->|Timeout
Mark Failed
Trigger Timeout Action| V
E1 -->|Not Timeout| E0
B1 -->|Failed
Mark Failed| V
V --> X[Record Execution Result]
X --> Y[Notify Dependent Tasks]
Y --> Z[End]
```
## Dependencies
- ~~[`github.com/pardnchiu/go-logger`](https://github.com/pardnchiu/go-logger)~~ (< v0.3.1)
For performance and stability, non-standard library packages are deprecated from `v0.3.1`, now using `log/slog`
## Usage
### Installation
> [!NOTE]
> Latest commit may change, recommended to use tagged versions
> Commits containing only documentation updates or non-functional changes will be rebased later
```bash
go get github.com/pardnchiu/go-cron@[VERSION]
git clone --depth 1 --branch [VERSION] https://github.com/pardnchiu/go-cron.git
```
### Initialization
#### Basic Usage
```go
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()
}
```
#### Task Dependencies
```go
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)
}
```
## Configuration
```go
type Config struct {
Location *time.Location // Timezone setting (default: time.Local)
}
```
## Supported Formats
### Standard
> 5-field format: `minute hour day month weekday`
> Supports range syntax `1-5` and `1,3,5`
```go
// 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)
```
### Custom
```go
// 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)
```
## Available Functions
### Scheduler Management
- `New()` - Create new scheduler instance
```go
scheduler, err := cron.New(config)
```
- Sets up task heap and communication channels
- `Start()` - Start scheduler instance
```go
scheduler.Start()
```
- Starts the scheduling loop
- `Stop()` - Stop scheduler
```go
ctx := 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
### Task Management
- `Add()` - Add scheduled task
```go
// 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 description
- `time.Duration`: Task execution timeout
- `func()`: 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 dependencies
- `func() error`: Has error return, supports dependencies
- `Remove()` - Cancel task schedule
```go
scheduler.Remove(taskID)
```
- Removes task from scheduling queue
- Safe to call regardless of scheduler state
- `RemoveAll()` - Remove all tasks
```go
scheduler.RemoveAll()
```
- Immediately removes all scheduled tasks
- Doesn't affect currently running tasks
- `List()` - Get task list
```go
tasks := scheduler.List()
```
## Task Dependencies
### Basic Usage
- 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)
### Dependency Examples
**Failure handling strategies**:
```go
// 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**:
```go
// 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**:
```go
// 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},
})
```
### Task Status
```go
const (
TaskPending // Waiting
TaskRunning // Running
TaskCompleted // Completed
TaskFailed // Failed / Timeout
)
```
## Timeout Mechanism
When execution time exceeds the set `Delay`:
- Interrupts task execution
- Triggers `OnDelay` function (if set)
- Logs timeout
- Continues with next schedule
### Features
- Timeout implemented using `context.WithTimeout`
- Timeout doesn't affect other task execution
- If action completes before timeout, timeout won't trigger
## Upcoming Features
### Enhanced Task Dependencies
- Status callbacks: Add `OnTimeout` and `OnFailed` callback functions for monitoring and responding to dependency task exception states
### Task Completion Trigger Refactor
- 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
## License
This project is licensed under [MIT](LICENSE).
## Star
[](https://www.star-history.com/#pardnchiu/go-cron&Date)
## Author

邱敬幃 Pardn Chiu
***
©️ 2025 [邱敬幃 Pardn Chiu](https://pardn.io)